on being anonymous
I am convinced that having the option of anonymous speech is extremely valuable to the modern development of free culture and society.
-
When people have no fear of personal repercussion, they can reveal corruptions and admit truths they otherwise never would. Their words are insightful and stupid, convincing and hurtful, hilarious and ridiculous. Try it; it's fun!
-
Nearly all forums and social networking platforms use the same pseudonymous username/password archetype, and nearly all of them have the same problems with drama, sockpuppets, and egotistical mods. Sometimes the price is worth it, and sometimes it is not.
+
When people do not have to fear personal repercussion, they can reveal corruptions and admit truths they otherwise never would. Their words are insightful and stupid, convincing and hurtful, hilarious and ridiculous. It's fun!
+
Nearly all forums and social networking platforms use the same pseudonymous username/password system, and nearly all of them have the same problems with drama, sockpuppets, and egotistical mods. Sometimes the price is worth it, and sometimes it is not.
I think people should have the option to interact with others anonymously if they wish, and further should always have the choice to filter or entirely block anonymously submitted content (or anything else!). I want people to decide for themselves what they see, not anyone else.
-
There are several online platforms that support anonymity, usually through a web browser, but most have terribly inefficient code, and their actual anonymity is often impotent window dressing, an afterthought. Logs of IP addresses are kept routinely, available for any admin (or anyone else who gains access to the server) to peruse.
+
There are several online platforms that support anonymity, usually through a web browser, but most have terribly inefficient code, and their actual anonymity is unreliable. Logs of IP addresses are kept routinely, available for any admin (or anyone else who gains access to the server) to peruse.
I think we can do better.
the hydrus network
-
So! I'm developing a program that helps people manage their files together anonymously. My primary concern is in enabling you to do what you want with your stuff, and that's it. You can share tags and files with other people, but you don't have to connect to anything if you don't want to. I don't plan to ever record metrics on users, nor serve ads, nor charge for my software.
+
So! I'm developing a program that helps people manage their files together anonymously. I want to help you do what you want with your stuff, and that's it. You can share tags and files with other people, but you don't have to connect to anything if you don't want to. The default is no sharing. I don't plan to ever record metrics on users, nor serve ads, nor charge for my software.
There are a number of new concepts involved, and it can get as complicated as you like. If you are totally new to the idea of personal media collections and tagging, I advise you start slow, walk through the getting started guides, and experiment doing different things. You'll be importing thousands of files and applying tens of thousands of tags in no time.
-
The client is chiefly a file database. It manages your media far better than an explorer window. Here's a screenshot of one of my test installs with a very general search:
+
The client is chiefly a file database. It manages your media far better than an explorer window or some online gallery. Here's a screenshot of one of my test installs with a very general search:
-
There is also a server that anyone can run to store files or tags for sharing between many users. I run a tag service with several million tags that you are welcome to access and contribute to.
+
As well as the client, there is also a server that anyone can run to store files or tags for sharing between many users. I run a tag service with several million tags that you are welcome to access and contribute to.
I'm working on adding peer-to-peer anonymous communication.
statement of principles
- No speech should be outlawed.
- Everyone should be able to control their own media diet.
- - The data on someone's computer and the logs of their network activity should be absolutely private.
+ - Computer data and network logs should be absolutely private.
None of the above are currently true, but I would love to live in a world where they were. My software is an attempt to move us a little closer.
I try to side with the person over the authority, the distributed over the centralised. I still use gmail and youtube just like pretty much everyone, but I would rather be using different systems, especially in ten years. No one seemed to be making what I wanted, so I decided to do it myself, and here we are.
diff --git a/help/screenshot_advanced_autocomplete.png b/help/screenshot_advanced_autocomplete.png
new file mode 100644
index 00000000..01c735be
Binary files /dev/null and b/help/screenshot_advanced_autocomplete.png differ
diff --git a/help/screenshot_advanced_autocomplete_thumb.png b/help/screenshot_advanced_autocomplete_thumb.png
new file mode 100644
index 00000000..1009361c
Binary files /dev/null and b/help/screenshot_advanced_autocomplete_thumb.png differ
diff --git a/help/screenshot_big_search.png b/help/screenshot_big_search.png
new file mode 100644
index 00000000..45368e12
Binary files /dev/null and b/help/screenshot_big_search.png differ
diff --git a/help/screenshot_big_search_thumb.png b/help/screenshot_big_search_thumb.png
new file mode 100644
index 00000000..2e0c02dc
Binary files /dev/null and b/help/screenshot_big_search_thumb.png differ
diff --git a/help/screenshot_booru.png b/help/screenshot_booru.png
new file mode 100644
index 00000000..1682c0f8
Binary files /dev/null and b/help/screenshot_booru.png differ
diff --git a/help/screenshot_booru_thumb.png b/help/screenshot_booru_thumb.png
new file mode 100644
index 00000000..287cea2a
Binary files /dev/null and b/help/screenshot_booru_thumb.png differ
diff --git a/help/screenshot_empty.png b/help/screenshot_empty.png
new file mode 100644
index 00000000..98ddda8d
Binary files /dev/null and b/help/screenshot_empty.png differ
diff --git a/help/screenshot_empty_thumb.png b/help/screenshot_empty_thumb.png
new file mode 100644
index 00000000..01d91a1c
Binary files /dev/null and b/help/screenshot_empty_thumb.png differ
diff --git a/help/screenshot_fullscreen_blame.png b/help/screenshot_fullscreen_blame.png
new file mode 100644
index 00000000..9693967e
Binary files /dev/null and b/help/screenshot_fullscreen_blame.png differ
diff --git a/help/screenshot_fullscreen_blame_thumb.png b/help/screenshot_fullscreen_blame_thumb.png
new file mode 100644
index 00000000..1a42392c
Binary files /dev/null and b/help/screenshot_fullscreen_blame_thumb.png differ
diff --git a/help/screenshot_general_search.png b/help/screenshot_general_search.png
new file mode 100644
index 00000000..b8eed77a
Binary files /dev/null and b/help/screenshot_general_search.png differ
diff --git a/help/screenshot_general_search_thumb.png b/help/screenshot_general_search_thumb.png
new file mode 100644
index 00000000..0fe756cf
Binary files /dev/null and b/help/screenshot_general_search_thumb.png differ
diff --git a/help/screenshot_gunnerkrigg_collect.png b/help/screenshot_gunnerkrigg_collect.png
new file mode 100644
index 00000000..ff73fa15
Binary files /dev/null and b/help/screenshot_gunnerkrigg_collect.png differ
diff --git a/help/screenshot_gunnerkrigg_collect_thumb.png b/help/screenshot_gunnerkrigg_collect_thumb.png
new file mode 100644
index 00000000..bdb817a0
Binary files /dev/null and b/help/screenshot_gunnerkrigg_collect_thumb.png differ
diff --git a/help/screenshot_video.png b/help/screenshot_video.png
new file mode 100644
index 00000000..41bfdd6d
Binary files /dev/null and b/help/screenshot_video.png differ
diff --git a/help/screenshot_video_thumb.png b/help/screenshot_video_thumb.png
new file mode 100644
index 00000000..dc8a95b1
Binary files /dev/null and b/help/screenshot_video_thumb.png differ
diff --git a/include/ClientConstants.py b/include/ClientConstants.py
index 2065dcf3..23e15b28 100755
--- a/include/ClientConstants.py
+++ b/include/ClientConstants.py
@@ -283,7 +283,7 @@ CLIENT_DEFAULT_OPTIONS[ 'shortcuts' ] = shortcuts
CLIENT_DEFAULT_OPTIONS[ 'confirm_client_exit' ] = False
-CLIENT_DEFAULT_OPTIONS[ 'default_tag_repository' ] = HC.LOCAL_TAG_SERVICE_IDENTIFIER
+CLIENT_DEFAULT_OPTIONS[ 'default_tag_repository' ] = HC.LOCAL_TAG_SERVICE_KEY
CLIENT_DEFAULT_OPTIONS[ 'default_tag_sort' ] = SORT_BY_LEXICOGRAPHIC_ASC
CLIENT_DEFAULT_OPTIONS[ 'pause_export_folders_sync' ] = False
@@ -1160,7 +1160,7 @@ class LocalBooruCache( object ):
def _RefreshShares( self ):
- self._local_booru_service = HC.app.Read( 'service', HC.LOCAL_BOORU_SERVICE_IDENTIFIER )
+ self._local_booru_service = HC.app.GetManager( 'services' ).GetService( HC.LOCAL_BOORU_SERVICE_IDENTIFIER.GetServiceKey() )
self._keys_to_infos = {}
@@ -2293,6 +2293,8 @@ class Service( HC.HydrusYAMLBase ):
self._name = name
self._info = info
+ self._lock = threading.Lock()
+
HC.pubsub.sub( self, 'ProcessServiceUpdates', 'service_updates_data' )
@@ -2565,29 +2567,14 @@ class ServiceManager( object ):
HC.pubsub.sub( self, 'RefreshServices', 'notify_new_services_data' )
- def GetName( self, service_key ):
-
- with self._lock:
-
- service = self._keys_to_services[ service_key ]
-
- return service.GetName()
-
-
-
def GetService( self, service_key ):
with self._lock: return self._keys_to_services[ service_key ]
- def GetType( self, service_key ):
+ def GetServices( self, types = HC.ALL_SERVICES ):
- with self._lock:
-
- service = self._keys_to_services[ service_key ]
-
- return service.GetType()
-
+ with self._lock: return [ service for service in self._keys_to_services.values() if service.GetType() in types ]
def RefreshServices( self ):
diff --git a/include/ClientController.py b/include/ClientController.py
index f4e3bde6..7e76b2b8 100755
--- a/include/ClientController.py
+++ b/include/ClientController.py
@@ -31,7 +31,7 @@ from twisted.internet import defer
ID_ANIMATED_EVENT_TIMER = wx.NewId()
ID_MAINTENANCE_EVENT_TIMER = wx.NewId()
-MAINTENANCE_PERIOD = 12 * 60
+MAINTENANCE_PERIOD = 5 * 60
class Controller( wx.App ):
@@ -112,7 +112,7 @@ The database will be locked while the backup occurs, which may lock up your gui
- def CurrentlyIdle( self ): return HC.GetNow() - self._timestamps[ 'last_user_db_use' ] > 30 * 60 # 30 mins since last user-initiated media_results query
+ def CurrentlyIdle( self ): return HC.GetNow() - self._timestamps[ 'last_user_action' ] > 30 * 60 # 30 mins since last canvas media swap
def EventPubSub( self, event ):
@@ -208,9 +208,10 @@ The database will be locked while the backup occurs, which may lock up your gui
self._managers = {}
+ self._managers[ 'services' ] = CC.ServiceManager()
+
self._managers[ 'hydrus_sessions' ] = HydrusSessions.HydrusSessionManagerClient()
self._managers[ 'local_booru' ] = CC.LocalBooruCache()
- self._managers[ 'services' ] = CC.ServiceManager()
self._managers[ 'tag_censorship' ] = HydrusTags.TagCensorshipManager()
self._managers[ 'tag_siblings' ] = HydrusTags.TagSiblingsManager()
self._managers[ 'tag_parents' ] = HydrusTags.TagParentsManager()
@@ -265,9 +266,9 @@ The database will be locked while the backup occurs, which may lock up your gui
HC.pubsub.pub( 'set_splash_text', 'fattening service info' )
- service_identifiers = self.Read( 'service_identifiers' )
+ services = self.GetManager( 'services' ).GetServices()
- for service_identifier in service_identifiers: self.Read( 'service_info', service_identifier )
+ for service in services: self.Read( 'service_info', service.GetKey() )
self._timestamps[ 'service_info_cache_fatten' ] = HC.GetNow()
@@ -314,12 +315,7 @@ The database will be locked while the backup occurs, which may lock up your gui
else: return text.lower()
- def Read( self, action, *args, **kwargs ):
-
- if action == 'media_results': self._timestamps[ 'last_user_db_use' ] = HC.GetNow()
-
- return self._Read( action, *args, **kwargs )
-
+ def Read( self, action, *args, **kwargs ): return self._Read( action, *args, **kwargs )
def ReadDaemon( self, action, *args, **kwargs ):
@@ -330,9 +326,11 @@ The database will be locked while the backup occurs, which may lock up your gui
return result
+ def ResetIdleTimer( self ): self._timestamps[ 'last_user_action' ] = HC.GetNow()
+
def RestartBooru( self ):
- service = self.Read( 'service', HC.LOCAL_BOORU_SERVICE_IDENTIFIER )
+ service = self.GetManager( 'services' ).GetService( HC.LOCAL_BOORU_SERVICE_IDENTIFIER.GetServiceKey() )
info = service.GetInfo()
@@ -506,7 +504,7 @@ Once it is done, the client will restart.'''
try:
- query_hash_ids = HC.app.Read( 'file_query_ids', search_context )
+ query_hash_ids = self.Read( 'file_query_ids', search_context )
query_hash_ids = list( query_hash_ids )
@@ -537,13 +535,13 @@ Once it is done, the client will restart.'''
sub_query_hash_ids = query_hash_ids[ last_i : i ]
- more_media_results = HC.app.Read( 'media_results_from_ids', file_service_identifier, sub_query_hash_ids )
+ more_media_results = self.Read( 'media_results_from_ids', file_service_identifier, sub_query_hash_ids )
media_results.extend( more_media_results )
HC.pubsub.pub( 'set_num_query_results', len( media_results ), len( query_hash_ids ) )
- HC.app.WaitUntilGoodTimeToUseGUIThread()
+ self.WaitUntilGoodTimeToUseGUIThread()
HC.pubsub.pub( 'file_query_done', query_key, media_results )
@@ -558,7 +556,7 @@ Once it is done, the client will restart.'''
self._timestamps[ 'last_check_idle_time' ] = HC.GetNow()
# this tests if we probably just woke up from a sleep
- if HC.GetNow() - last_time_this_ran > MAINTENANCE_PERIOD * 1.2: return
+ if HC.GetNow() - last_time_this_ran > MAINTENANCE_PERIOD + ( 5 * 60 ): return
if self.CurrentlyIdle(): self.MaintainDB()
diff --git a/include/ClientDB.py b/include/ClientDB.py
index 96d871ed..c51d9b7c 100755
--- a/include/ClientDB.py
+++ b/include/ClientDB.py
@@ -1316,11 +1316,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
c.execute( 'REPLACE INTO hydrus_sessions ( service_id, session_key, expiry ) VALUES ( ?, ?, ? );', ( service_id, sqlite3.Binary( session_key ), expiry ) )
- def _AddService( self, c, service_identifier, info ):
-
- service_key = service_identifier.GetServiceKey()
- service_type = service_identifier.GetType()
- name = service_identifier.GetName()
+ def _AddService( self, c, service_key, service_type, name, info ):
if service_type in HC.LOCAL_SERVICES:
@@ -2497,10 +2493,6 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
service_type = service_identifier.GetType()
- repository = self._GetService( c, service_id )
-
- account = repository.GetInfo( 'account' )
-
if service_type == HC.TAG_REPOSITORY:
updates = []
@@ -2657,57 +2649,30 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
return reason_id
- def _GetService( self, c, parameter ):
-
- try:
-
- if type( parameter ) == int: service_id = parameter
- elif type( parameter ) == HC.ClientServiceIdentifier: service_id = self._GetServiceId( c, parameter )
-
- except: raise Exception( 'Service error in database.' )
-
- result = c.execute( 'SELECT service_key, service_type, name, info FROM services WHERE service_id = ?;', ( service_id, ) ).fetchone()
-
- if result is None: raise Exception( 'Service error in database.' )
-
- ( service_key, service_type, name, info ) = result
-
- service = CC.Service( service_key, service_type, name, info )
-
- return service
-
-
def _GetServices( self, c, limited_types = HC.ALL_SERVICES ):
- service_ids = [ service_id for ( service_id, ) in c.execute( 'SELECT service_id FROM services WHERE service_type IN ' + HC.SplayListForDB( limited_types ) + ';' ) ]
+ services = []
- services = [ self._GetService( c, service_id ) for service_id in service_ids ]
+ for result in c.execute( 'SELECT service_key, service_type, name, info FROM services WHERE service_type IN ' + HC.SplayListForDB( limited_types ) + ';' ):
+
+ ( service_key, service_type, name, info ) = result
+
+ services.append( CC.Service( service_key, service_type, name, info ) )
+
return services
def _GetServiceId( self, c, parameter ):
- if type( parameter ) in ( str, unicode ):
-
- result = c.execute( 'SELECT service_id FROM services WHERE name = ?;', ( parameter, ) ).fetchone()
-
- if result is None: raise Exception( 'Service id error in database' )
-
- ( service_id, ) = result
-
- elif type( parameter ) == HC.ClientServiceIdentifier:
-
- service_type = parameter.GetType()
-
- service_key = parameter.GetServiceKey()
-
- result = c.execute( 'SELECT service_id FROM services WHERE service_key = ?;', ( sqlite3.Binary( service_key ), ) ).fetchone()
-
- if result is None: raise Exception( 'Service id error in database' )
-
- ( service_id, ) = result
-
+ if type( parameter ) == HC.ClientServiceIdentifier: service_key = parameter.GetServiceKey()
+ else: service_key = parameter
+
+ result = c.execute( 'SELECT service_id FROM services WHERE service_key = ?;', ( sqlite3.Binary( service_key ), ) ).fetchone()
+
+ if result is None: raise Exception( 'Service id error in database' )
+
+ ( service_id, ) = result
return service_id
@@ -2727,11 +2692,11 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
def _GetServiceIdentifiers( self, c, limited_types = HC.ALL_SERVICES ): return { HC.ClientServiceIdentifier( service_key, service_type, name ) for ( service_key, service_type, name ) in c.execute( 'SELECT service_key, service_type, name FROM services WHERE service_type IN ' + HC.SplayListForDB( limited_types ) + ';' ) }
- def _GetServiceInfo( self, c, service_identifier ):
+ def _GetServiceInfo( self, c, service_key ):
- service_id = self._GetServiceId( c, service_identifier )
+ service_id = self._GetServiceId( c, service_key )
- service_type = service_identifier.GetType()
+ service_type = self._GetServiceType( c, service_id )
if service_type == HC.LOCAL_FILE: info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_TOTAL_SIZE, HC.SERVICE_INFO_NUM_DELETED_FILES }
elif service_type == HC.FILE_REPOSITORY: info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_TOTAL_SIZE, HC.SERVICE_INFO_NUM_DELETED_FILES, HC.SERVICE_INFO_NUM_THUMBNAILS, HC.SERVICE_INFO_NUM_THUMBNAILS_LOCAL }
@@ -3022,14 +2987,6 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
result = { dump_name.decode( 'hex' ) : data for ( dump_name, data ) in result.items() }
- if dump_type == YAML_DUMP_ID_SUBSCRIPTION:
-
- for ( dump_name, data ) in result.items():
-
- data[ 'advanced_tag_options' ] = dict( data[ 'advanced_tag_options' ] )
-
-
-
else:
if dump_type == YAML_DUMP_ID_LOCAL_BOORU: dump_name = dump_name.encode( 'hex' )
@@ -3046,15 +3003,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
if result is None: raise Exception( dump_name + ' was not found!' )
- else:
-
- ( result, ) = result
-
- if dump_type == YAML_DUMP_ID_SUBSCRIPTION:
-
- result[ 'advanced_tag_options' ] = dict( result[ 'advanced_tag_options' ] )
-
-
+ else: ( result, ) = result
return result
@@ -3827,14 +3776,12 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
def _ResetService( self, c, service_identifier ):
- service_name = service_identifier.GetName()
+ name = service_identifier.GetName()
service_type = service_identifier.GetType()
service_id = self._GetServiceId( c, service_identifier )
- service = self._GetService( c, service_id )
-
- info = service.GetInfo()
+ ( info, ) = c.execute( 'SELECT info FROM services WHERE service_id = ?;', ( service_id, ) ).fetchone()
c.execute( 'DELETE FROM services WHERE service_id = ?;', ( service_id, ) )
@@ -3852,14 +3799,16 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
self.pub_after_commit( 'notify_restart_repo_sync_daemon' )
- self._AddService( c, service_identifier, info )
+ service_key = service_identifier.GetServiceKey()
+
+ self._AddService( c, service_key, service_type, name, info )
self.pub_service_updates_after_commit( { service_identifier : [ HC.ServiceUpdate( HC.SERVICE_UPDATE_RESET ) ] } )
self.pub_after_commit( 'notify_new_pending' )
self.pub_after_commit( 'notify_new_services_data' )
self.pub_after_commit( 'notify_new_services_gui' )
self.pub_after_commit( 'permissions_are_stale' )
- HC.ShowText( 'reset ' + service_name )
+ HC.ShowText( 'reset ' + name )
def _SetTagCensorship( self, c, info ):
@@ -3901,8 +3850,6 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
c.execute( 'DELETE FROM yaml_dumps WHERE dump_type = ? AND dump_name = ?;', ( dump_type, dump_name ) )
- if dump_type == YAML_DUMP_ID_SUBSCRIPTION: data[ 'advanced_tag_options' ] = data[ 'advanced_tag_options' ].items()
-
try: c.execute( 'INSERT INTO yaml_dumps ( dump_type, dump_name, dump ) VALUES ( ?, ?, ? );', ( dump_type, dump_name, data ) )
except:
@@ -4290,11 +4237,10 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
server_admin_service_id = self._GetServiceId( c, server_admin_service_identifier )
- server_admin = self._GetService( c, server_admin_service_id )
+ ( server_admin_info, ) = c.execute( 'SELECT info FROM services WHERE service_id = ?;', ( server_admin_service_id, ) ).fetchone()
- server_admin_credentials = server_admin.GetCredentials()
-
- ( host, server_admin_port ) = server_admin_credentials.GetAddress()
+ host = server_admin_info[ 'host' ]
+ server_admin_port = server_admin_info[ 'port' ]
recalc_combined_mappings = False
@@ -4315,9 +4261,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
name = HC.service_string_lookup[ service_type ] + ' at ' + host + ':' + HC.u( info[ 'port' ] )
- client_service_identifier = HC.ClientServiceIdentifier( service_key, service_type, name )
-
- self._AddService( c, client_service_identifier, info )
+ self._AddService( c, service_key, service_type, name, info )
elif action == HC.DELETE:
@@ -4406,7 +4350,11 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
( service_identifier, info ) = details
- self._AddService( c, service_identifier, info )
+ service_key = service_identifier.GetServiceKey()
+ service_type = service_identifier.GetType()
+ name = service_identifier.GetName()
+
+ self._AddService( c, service_key, service_type, name, info )
elif action == HC.DELETE:
@@ -4570,10 +4518,10 @@ class DB( ServiceDB ):
raise
- self._local_file_service_id = self._GetServiceId( c, HC.LOCAL_FILE_SERVICE_IDENTIFIER )
- self._local_tag_service_id = self._GetServiceId( c, HC.LOCAL_TAG_SERVICE_IDENTIFIER )
- self._combined_file_service_id = self._GetServiceId( c, HC.COMBINED_FILE_SERVICE_IDENTIFIER )
- self._combined_tag_service_id = self._GetServiceId( c, HC.COMBINED_TAG_SERVICE_IDENTIFIER )
+ self._local_file_service_id = self._GetServiceId( c, HC.LOCAL_FILE_SERVICE_KEY )
+ self._local_tag_service_id = self._GetServiceId( c, HC.LOCAL_TAG_SERVICE_KEY )
+ self._combined_file_service_id = self._GetServiceId( c, HC.COMBINED_FILE_SERVICE_KEY )
+ self._combined_tag_service_id = self._GetServiceId( c, HC.COMBINED_TAG_SERVICE_KEY )
options = self._GetOptions( c )
@@ -4820,9 +4768,12 @@ class DB( ServiceDB ):
for init_service_identifier in init_service_identifiers:
+ service_key = init_service_identifier.GetServiceKey()
+ service_type = init_service_identifier.GetType()
+ name = init_service_identifier.GetName()
info = {}
- self._AddService( c, init_service_identifier, info )
+ self._AddService( c, service_key, service_type, name, info )
c.executemany( 'INSERT INTO yaml_dumps VALUES ( ?, ?, ? );', ( ( YAML_DUMP_ID_REMOTE_BOORU, name, booru ) for ( name, booru ) in CC.DEFAULT_BOORUS.items() ) )
@@ -4940,6 +4891,9 @@ class DB( ServiceDB ):
if version == 114:
+ service_key = HC.LOCAL_BOORU_SERVICE_IDENTIFIER.GetServiceKey()
+ service_type = HC.LOCAL_BOORU_SERVICE_IDENTIFIER.GetType()
+ name = HC.LOCAL_BOORU_SERVICE_IDENTIFIER.GetName()
info = {}
self._AddService( c, HC.LOCAL_BOORU_SERVICE_IDENTIFIER, info )
@@ -5064,6 +5018,35 @@ class DB( ServiceDB ):
c.execute( 'UPDATE services SET info = ? WHERE service_id = ?;', ( info, service_id ) )
+ if version == 125:
+
+ ( HC.options, ) = c.execute( 'SELECT options FROM options;' ).fetchone()
+
+ HC.options[ 'default_tag_repository' ] = HC.options[ 'default_tag_repository' ].GetServiceKey()
+
+ c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
+
+ #
+
+ results = c.execute( 'SELECT * FROM yaml_dumps WHERE dump_type = ?;', ( YAML_DUMP_ID_SUBSCRIPTION, ) ).fetchall()
+
+ for ( dump_type, dump_name, dump ) in results:
+
+ advanced_tag_options = dump[ 'advanced_tag_options' ]
+
+ new_advanced_tag_options = {}
+
+ for ( service_identifier, namespaces ) in advanced_tag_options:
+
+ new_advanced_tag_options[ service_identifier.GetServiceKey() ] = namespaces
+
+
+ dump[ 'advanced_tag_options' ] = new_advanced_tag_options
+
+ c.execute( 'UPDATE yaml_dumps SET dump = ? WHERE dump_type = ? and dump_name = ?;', ( dump, dump_type, dump_name ) )
+
+
+
c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
HC.is_db_updated = True
@@ -7096,7 +7079,6 @@ class DB( ServiceDB ):
elif action == 'ratings_media_result': result = self._GetRatingsMediaResult( c, *args, **kwargs )
elif action == 'remote_booru': result = self._GetYAMLDump( c, YAML_DUMP_ID_REMOTE_BOORU, *args, **kwargs )
elif action == 'remote_boorus': result = self._GetYAMLDump( c, YAML_DUMP_ID_REMOTE_BOORU )
- elif action == 'service': result = self._GetService( c, *args, **kwargs )
elif action == 'service_identifiers': result = self._GetServiceIdentifiers( c, *args, **kwargs )
elif action == 'service_info': result = self._GetServiceInfo( c, *args, **kwargs )
elif action == 'services': result = self._GetServices( c, *args, **kwargs )
@@ -7557,7 +7539,7 @@ def DAEMONDownloadFiles():
if service_identifier == HC.LOCAL_FILE_SERVICE_IDENTIFIER: break
- try: file_repository = HC.app.ReadDaemon( 'service', service_identifier )
+ try: file_repository = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
except: continue
HC.pubsub.pub( 'downloads_status', HC.ConvertIntToPrettyString( num_downloads ) + ' file downloads' )
@@ -7611,7 +7593,7 @@ def DAEMONDownloadThumbnails():
if len( thumbnail_hashes_i_need ) > 0:
- try: file_repository = HC.app.ReadDaemon( 'service', service_identifier )
+ try: file_repository = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
except: continue
if file_repository.CanDownload():
@@ -7711,7 +7693,7 @@ def DAEMONResizeThumbnails():
def DAEMONSynchroniseAccounts():
- services = HC.app.ReadDaemon( 'services', HC.RESTRICTED_SERVICES )
+ services = HC.app.GetManager( 'services' ).GetServices( HC.RESTRICTED_SERVICES )
do_notify = False
@@ -7771,7 +7753,7 @@ def DAEMONSynchroniseMessages():
service_type = service_identifier.GetType()
- try: service = HC.app.ReadDaemon( 'service', service_identifier )
+ try: service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
except: continue
if service.CanCheck():
@@ -7794,7 +7776,7 @@ def DAEMONSynchroniseMessages():
HC.app.WriteSynchronous( 'contact_associated', service_identifier )
- service = HC.app.ReadDaemon( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
contact = service.GetContact()
@@ -7893,7 +7875,7 @@ def DAEMONSynchroniseMessages():
my_public_key = contact_from.GetPublicKey()
my_contact_key = contact_from.GetContactKey()
- my_message_depot = HC.app.ReadDaemon( 'service', contact_from )
+ my_message_depot = HC.app.GetManager( 'services' ).GetService( contact_from.GetServiceKey() )
from_connection = my_message_depot.GetConnection()
@@ -7957,7 +7939,7 @@ def DAEMONSynchroniseRepositories():
service_type = service_identifier.GetType()
service_key = service_identifier.GetServiceKey()
- try: service = HC.app.ReadDaemon( 'service', service_identifier )
+ try: service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
except: continue
info = service.GetInfo()
@@ -7966,19 +7948,34 @@ def DAEMONSynchroniseRepositories():
if service.CanDownloadUpdate():
- message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, 'checking ' + name + ' repository' )
+ job_key = HC.JobKey( pausable = True, cancellable = False )
+
+ message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, 'checking ' + name + ' repository', job_key )
HC.pubsub.pub( 'message', message )
while service.CanDownloadUpdate():
+ while job_key.IsPaused():
+
+ message.SetInfo( 'text', 'paused' )
+
+ time.sleep( 1 )
+
+ if HC.shutdown: raise Exception( 'application shutting down!' )
+
+ if message.IsClosed(): return
+
+
while HC.options[ 'pause_repo_sync' ]:
- message.SetInfo( 'text', 'Repository synchronisation paused' )
+ message.SetInfo( 'text', 'repository synchronisation paused' )
time.sleep( 5 )
- if HC.shutdown: raise Exception( 'Application shutting down!' )
+ if HC.shutdown: raise Exception( 'application shutting down!' )
+
+ if message.IsClosed(): return
if HC.repos_changed:
@@ -7990,7 +7987,7 @@ def DAEMONSynchroniseRepositories():
- if HC.shutdown: raise Exception( 'Application shutting down!' )
+ if HC.shutdown: raise Exception( 'application shutting down!' )
now = HC.GetNow()
@@ -8036,7 +8033,7 @@ def DAEMONSynchroniseRepositories():
time.sleep( 0.10 )
- try: service = HC.app.ReadDaemon( 'service', service_identifier )
+ try: service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
except: break
@@ -8045,19 +8042,34 @@ def DAEMONSynchroniseRepositories():
if service.CanProcessUpdate():
- message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, 'processing ' + name + ' repository' )
+ job_key = HC.JobKey( pausable = True, cancellable = False )
+
+ message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, 'processing ' + name + ' repository', job_key )
HC.pubsub.pub( 'message', message )
while service.CanProcessUpdate():
+ while job_key.IsPaused():
+
+ message.SetInfo( 'text', 'paused' )
+
+ time.sleep( 1 )
+
+ if HC.shutdown: raise Exception( 'application shutting down!' )
+
+ if message.IsClosed(): return
+
+
while HC.options[ 'pause_repo_sync' ]:
- message.SetInfo( 'text', 'Repository synchronisation paused' )
+ message.SetInfo( 'text', 'repository synchronisation paused' )
time.sleep( 5 )
- if HC.shutdown: raise Exception( 'Application shutting down!' )
+ if HC.shutdown: raise Exception( 'application shutting down!' )
+
+ if message.IsClosed(): return
if HC.repos_changed:
@@ -8069,7 +8081,7 @@ def DAEMONSynchroniseRepositories():
- if HC.shutdown: raise Exception( 'Application shutting down!' )
+ if HC.shutdown: raise Exception( 'application shutting down!' )
now = HC.GetNow()
@@ -8168,7 +8180,7 @@ def DAEMONSynchroniseRepositories():
time.sleep( 0.10 )
- try: service = HC.app.ReadDaemon( 'service', service_identifier )
+ try: service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
except: break
@@ -8225,7 +8237,9 @@ def DAEMONSynchroniseSubscriptions():
try:
- message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, 'checking ' + name + ' subscription' )
+ job_key = HC.JobKey( pausable = True, cancellable = False )
+
+ message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, 'checking ' + name + ' subscription', job_key )
HC.pubsub.pub( 'message', message )
@@ -8291,6 +8305,17 @@ def DAEMONSynchroniseSubscriptions():
while True:
+ while job_key.IsPaused():
+
+ message.SetInfo( 'text', 'paused' )
+
+ time.sleep( 1 )
+
+ if HC.shutdown: return
+
+ if message.IsClosed(): return
+
+
while HC.options[ 'pause_subs_sync' ]:
message.SetInfo( 'text', 'subscriptions paused' )
@@ -8299,6 +8324,8 @@ def DAEMONSynchroniseSubscriptions():
if HC.shutdown: return
+ if message.IsClosed(): return
+
if HC.subs_changed:
message.Close()
@@ -8467,6 +8494,8 @@ def DAEMONSynchroniseSubscriptions():
message.SetInfo( 'hashes', successful_hashes )
message.SetInfo( 'mode', 'files' )
+ job_key.SetPausable( False )
+
else: message.Close()
last_checked = now
@@ -8504,13 +8533,17 @@ def DAEMONSynchroniseSubscriptions():
def DAEMONUPnP():
- local_ip = HydrusNATPunch.GetLocalIP()
+ try:
+
+ local_ip = HydrusNATPunch.GetLocalIP()
+
+ current_mappings = HydrusNATPunch.GetUPnPMappings()
+
+ our_mappings = { ( internal_client, internal_port ) : external_port for ( description, internal_client, internal_port, external_ip_address, external_port, protocol, enabled ) in current_mappings }
+
+ except: return # This IGD probably doesn't support UPnP, so don't spam the user with errors they can't fix!
- current_mappings = HydrusNATPunch.GetUPnPMappings()
-
- our_mappings = { ( internal_client, internal_port ) : external_port for ( description, internal_client, internal_port, external_ip_address, external_port, protocol, enabled ) in current_mappings }
-
- services = HC.app.ReadDaemon( 'services', ( HC.LOCAL_BOORU, ) )
+ services = HC.app.GetManager( 'services' ).GetServices( ( HC.LOCAL_BOORU, ) )
for service in services:
diff --git a/include/ClientGUI.py b/include/ClientGUI.py
index 6fdd9c57..b5f1bf90 100755
--- a/include/ClientGUI.py
+++ b/include/ClientGUI.py
@@ -144,17 +144,17 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
try:
- job_key = HC.JobKey()
+ job_key = HC.JobKey( pausable = False, cancellable = False )
- message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, 'gathering pending and petitioned' )
+ message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, 'gathering pending and petitioned', job_key )
HC.pubsub.pub( 'message', message )
result = HC.app.Read( 'pending', service_identifier )
- service_type = service_identifier.GetType()
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
- service = HC.app.Read( 'service', service_identifier )
+ service_type = service.GetType()
if service_type == HC.FILE_REPOSITORY:
@@ -172,7 +172,6 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
i = 1
- message.SetInfo( 'job_key', job_key )
message.SetInfo( 'range', gauge_range )
message.SetInfo( 'value', i )
message.SetInfo( 'text', 'connecting to repository' )
@@ -325,7 +324,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
subject_access_key = dlg.GetValue().decode( 'hex' )
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
response = service.Request( HC.GET, 'account_info', { 'subject_access_key' : subject_access_key.encode( 'hex' ) } )
@@ -338,178 +337,193 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
def _AutoRepoSetup( self ):
+ def do_it():
+
+ edit_log = []
+
+ tag_repo_identifier = HC.ClientServiceIdentifier( os.urandom( 32 ), HC.TAG_REPOSITORY, 'public tag repository' )
+
+ tag_repo_info = {}
+
+ tag_repo_info[ 'host' ] = 'hydrus.no-ip.org'
+ tag_repo_info[ 'port' ] = 45871
+ tag_repo_info[ 'access_key' ] = '4a285629721ca442541ef2c15ea17d1f7f7578b0c3f4f5f2a05f8f0ab297786f'.decode( 'hex' )
+
+ edit_log.append( ( HC.ADD, ( tag_repo_identifier, tag_repo_info ) ) )
+
+ file_repo_identifier = HC.ClientServiceIdentifier( os.urandom( 32 ), HC.FILE_REPOSITORY, 'read-only art file repository' )
+
+ file_repo_info = {}
+
+ file_repo_info[ 'host' ] = 'hydrus.no-ip.org'
+ file_repo_info[ 'port' ] = 45872
+ file_repo_info[ 'access_key' ] = '8f8a3685abc19e78a92ba61d84a0482b1cfac176fd853f46d93fe437a95e40a5'.decode( 'hex' )
+
+ edit_log.append( ( HC.ADD, ( file_repo_identifier, file_repo_info ) ) )
+
+ HC.app.Write( 'update_services', edit_log )
+
+ HC.ShowText( 'Auto repo setup done! Check services->review services to see your new services.' )
+
+
message = 'This will attempt to set up your client with my repositories\' credentials, letting you tag on the public tag repository and see some files.'
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
- if dlg.ShowModal() == wx.ID_YES:
-
- edit_log = []
-
- tag_repo_identifier = HC.ClientServiceIdentifier( os.urandom( 32 ), HC.TAG_REPOSITORY, 'public tag repository' )
-
- tag_repo_info = {}
-
- tag_repo_info[ 'host' ] = 'hydrus.no-ip.org'
- tag_repo_info[ 'port' ] = 45871
- tag_repo_info[ 'access_key' ] = '4a285629721ca442541ef2c15ea17d1f7f7578b0c3f4f5f2a05f8f0ab297786f'.decode( 'hex' )
-
- edit_log.append( ( HC.ADD, ( tag_repo_identifier, tag_repo_info ) ) )
-
- file_repo_identifier = HC.ClientServiceIdentifier( os.urandom( 32 ), HC.FILE_REPOSITORY, 'read-only art file repository' )
-
- file_repo_info = {}
-
- file_repo_info[ 'host' ] = 'hydrus.no-ip.org'
- file_repo_info[ 'port' ] = 45872
- file_repo_info[ 'access_key' ] = '8f8a3685abc19e78a92ba61d84a0482b1cfac176fd853f46d93fe437a95e40a5'.decode( 'hex' )
-
- edit_log.append( ( HC.ADD, ( file_repo_identifier, file_repo_info ) ) )
-
- HC.app.Write( 'update_services', edit_log )
-
- HC.ShowText( 'Auto repo setup done!' )
-
+ if dlg.ShowModal() == wx.ID_YES: HydrusThreading.CallToThread( do_it )
def _AutoServerSetup( self ):
- host = '127.0.0.1'
- port = HC.DEFAULT_SERVER_ADMIN_PORT
+ def do_it():
+
+ host = '127.0.0.1'
+ port = HC.DEFAULT_SERVER_ADMIN_PORT
+
+ try:
+
+ connection = httplib.HTTPConnection( '127.0.0.1', HC.DEFAULT_SERVER_ADMIN_PORT, timeout = 20 )
+
+ connection.connect()
+
+ connection.close()
+
+ already_running = True
+
+ except:
+
+ already_running = False
+
+
+ if already_running:
+
+ HC.ShowText( 'The server appears to be already running. Either that, or something else is using port ' + HC.u( HC.DEFAULT_SERVER_ADMIN_PORT ) + '.' )
+
+ return
+
+ else:
+
+ try:
+
+ HC.ShowText( 'starting server' )
+
+ my_scriptname = sys.argv[0]
+
+ if my_scriptname.endswith( 'pyw' ): subprocess.Popen( [ 'pythonw', HC.BASE_DIR + os.path.sep + 'server.pyw' ] )
+ else:
+
+ # The problem here is that, for mystical reasons, a PyInstaller exe can't launch another using subprocess, so we do it via explorer.
+
+ subprocess.Popen( [ 'explorer', HC.BASE_DIR + os.path.sep + 'server.exe' ] )
+
+
+ time.sleep( 10 ) # give it time to init its db
+
+ except:
+
+ HC.ShowText( 'I tried to start the server, but something failed!' )
+ HC.ShowText( traceback.format_exc() )
+
+ return
+
+
+
+ HC.ShowText( 'creating admin service' )
+
+ edit_log = []
+
+ admin_service_identifier = HC.ClientServiceIdentifier( os.urandom( 32 ), HC.SERVER_ADMIN, 'local server admin' )
+
+ admin_service_info = {}
+
+ admin_service_info[ 'host' ] = host
+ admin_service_info[ 'port' ] = port
+ admin_service_info[ 'access_key' ] = ''
+
+ edit_log.append( ( HC.ADD, ( admin_service_identifier, admin_service_info ) ) )
+
+ HC.app.Write( 'update_services', edit_log )
+
+ time.sleep( 5 )
+
+ i = 0
+
+ while True:
+
+ time.sleep( i + 1 )
+
+ try:
+
+ service = HC.app.GetManager( 'services' ).GetService( admin_service_identifier.GetServiceKey() )
+
+ break
+
+ except: pass
+
+ i += 1
+
+ if i > 5:
+
+ HC.ShowText( 'For some reason, I could not add the new server to the db! Perhaps it is very busy. Please email the hydrus developer, or sort it out yourself!' )
+
+ return
+
+
+
+ #
+
+ response = service.Request( HC.GET, 'init' )
+
+ access_key = response[ 'access_key' ]
+
+ update = { 'access_key' : access_key }
+
+ edit_log = [ ( HC.EDIT, ( admin_service_identifier, ( admin_service_identifier, update ) ) ) ]
+
+ HC.app.Write( 'update_services', edit_log )
+
+ HC.ShowText( 'admin service initialised' )
+
+ wx.CallAfter( ClientGUICommon.ShowKeys, 'access', ( access_key, ) )
+
+ time.sleep( 5 )
+
+ service = HC.app.GetManager( 'services' ).GetService( admin_service_identifier.GetServiceKey() )
+
+ #
+
+ HC.ShowText( 'creating tag and file services' )
+
+ tag_server_service_identifier = HC.ServerServiceIdentifier( os.urandom( 32 ), HC.TAG_REPOSITORY )
+
+ tag_options = HC.DEFAULT_OPTIONS[ HC.TAG_REPOSITORY ]
+ tag_options[ 'port' ] = HC.DEFAULT_SERVICE_PORT
+
+ file_server_service_identifier = HC.ServerServiceIdentifier( os.urandom( 32 ), HC.FILE_REPOSITORY )
+
+ file_options = HC.DEFAULT_OPTIONS[ HC.FILE_REPOSITORY ]
+ file_options[ 'port' ] = HC.DEFAULT_SERVICE_PORT + 1
+
+ edit_log = []
+
+ edit_log.append( ( HC.ADD, ( tag_server_service_identifier, tag_options ) ) )
+ edit_log.append( ( HC.ADD, ( file_server_service_identifier, file_options ) ) )
+
+ response = service.Request( HC.POST, 'services', { 'edit_log' : edit_log } )
+
+ service_identifiers_to_access_keys = dict( response[ 'service_identifiers_to_access_keys' ] )
+
+ HC.app.Write( 'update_server_services', admin_service_identifier, edit_log, service_identifiers_to_access_keys )
+
+ HC.ShowText( 'Done! Check services->review services to see your new server and its services.' )
+
message = 'This will attempt to start the server in the same install directory as this client, initialise it, and store the resultant admin accounts in the client.'
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
- if dlg.ShowModal() == wx.ID_YES:
-
- try:
-
- connection = httplib.HTTPConnection( '127.0.0.1', HC.DEFAULT_SERVER_ADMIN_PORT, timeout = 20 )
-
- connection.connect()
-
- connection.close()
-
- already_running = True
-
- except:
-
- already_running = False
-
-
- if already_running:
-
- message = 'The server appears to be already running. Either that, or something else is using port ' + HC.u( HC.DEFAULT_SERVER_ADMIN_PORT ) + '.' + os.linesep + 'Would you like to try to initialise the server that is already running?'
-
- with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
-
- if dlg.ShowModal() != wx.ID_YES: return
-
-
- else:
-
- try:
-
- my_scriptname = sys.argv[0]
-
- if my_scriptname.endswith( 'pyw' ): subprocess.Popen( [ 'pythonw', HC.BASE_DIR + os.path.sep + 'server.pyw' ] )
- else:
-
- # The problem here is that, for mystical reasons, a PyInstaller exe can't launch another using subprocess, so we do it via explorer.
-
- subprocess.Popen( [ 'explorer', HC.BASE_DIR + os.path.sep + 'server.exe' ] )
-
-
- time.sleep( 10 ) # give it time to init its db
-
- except:
-
- wx.MessageBox( 'I tried to start the server, but something failed!' )
- wx.MessageBox( traceback.format_exc() )
-
- return
-
-
-
- edit_log = []
-
- admin_service_identifier = HC.ClientServiceIdentifier( os.urandom( 32 ), HC.SERVER_ADMIN, 'local server admin' )
-
- admin_service_info = {}
-
- admin_service_info[ 'host' ] = host
- admin_service_info[ 'port' ] = port
- admin_service_info[ 'access_key' ] = ''
-
- edit_log.append( ( HC.ADD, ( admin_service_identifier, admin_service_info ) ) )
-
- HC.app.Write( 'update_services', edit_log )
-
- i = 0
-
- while True:
-
- time.sleep( i + 1 )
-
- try:
-
- service = HC.app.Read( 'service', admin_service_identifier )
-
- break
-
- except: pass
-
- i += 1
-
- if i > 5:
-
- wx.MessageBox( 'For some reason, I could not add the new server to the db! Perhaps it is very busy. Please contact the administrator, or sort it out yourself!' )
-
- return
-
-
-
- #
-
- response = service.Request( HC.GET, 'init' )
-
- access_key = response[ 'access_key' ]
-
- update = { 'access_key' : access_key }
-
- edit_log = [ ( HC.EDIT, ( admin_service_identifier, ( admin_service_identifier, update ) ) ) ]
-
- HC.app.Write( 'update_services', edit_log )
-
- ClientGUICommon.ShowKeys( 'access', ( access_key, ) )
-
- #
-
- tag_server_service_identifier = HC.ServerServiceIdentifier( os.urandom( 32 ), HC.TAG_REPOSITORY )
-
- tag_options = HC.DEFAULT_OPTIONS[ HC.TAG_REPOSITORY ]
- tag_options[ 'port' ] = HC.DEFAULT_SERVICE_PORT
-
- file_server_service_identifier = HC.ServerServiceIdentifier( os.urandom( 32 ), HC.FILE_REPOSITORY )
-
- file_options = HC.DEFAULT_OPTIONS[ HC.FILE_REPOSITORY ]
- file_options[ 'port' ] = HC.DEFAULT_SERVICE_PORT + 1
-
- edit_log = []
-
- edit_log.append( ( HC.ADD, ( tag_server_service_identifier, tag_options ) ) )
- edit_log.append( ( HC.ADD, ( file_server_service_identifier, file_options ) ) )
-
- response = service.Request( HC.POST, 'services', { 'edit_log' : edit_log } )
-
- service_identifiers_to_access_keys = dict( response[ 'service_identifiers_to_access_keys' ] )
-
- HC.app.Write( 'update_server_services', admin_service_identifier, edit_log, service_identifiers_to_access_keys )
-
- wx.MessageBox( 'Done! Check services->review services to see your new server and its services.' )
-
+ if dlg.ShowModal() == wx.ID_YES: HydrusThreading.CallToThread( do_it )
@@ -521,7 +535,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
if dlg.ShowModal() == wx.ID_YES:
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
with wx.BusyCursor(): service.Request( HC.POST, 'backup' )
@@ -611,7 +625,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
hash = dlg.GetValue().decode( 'hex' )
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
with wx.BusyCursor(): response = service.Request( HC.GET, 'ip', { 'hash' : hash.encode( 'hex' ) } )
@@ -746,13 +760,13 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
def view():
- services = HC.app.Read( 'services' )
+ services = HC.app.GetManager( 'services' ).GetServices()
- tag_repositories = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.TAG_REPOSITORY ]
+ tag_repositories = [ service for service in services if service.GetType() == HC.TAG_REPOSITORY ]
petition_resolve_tag_service_identifiers = [ repository.GetServiceIdentifier() for repository in tag_repositories if repository.GetInfo( 'account' ).HasPermission( HC.RESOLVE_PETITIONS ) ]
- file_repositories = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.FILE_REPOSITORY ]
+ file_repositories = [ service for service in services if service.GetType() == HC.FILE_REPOSITORY ]
file_service_identifiers = [ repository.GetServiceIdentifier() for repository in file_repositories ]
petition_resolve_file_service_identifiers = [ repository.GetServiceIdentifier() for repository in file_repositories if repository.GetInfo( 'account' ).HasPermission( HC.RESOLVE_PETITIONS ) ]
@@ -897,15 +911,15 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
def admin():
- services = HC.app.Read( 'services' )
+ services = HC.app.GetManager( 'services' ).GetServices()
- tag_repositories = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.TAG_REPOSITORY ]
+ tag_repositories = [ service for service in services if service.GetType() == HC.TAG_REPOSITORY ]
admin_tag_service_identifiers = [ repository.GetServiceIdentifier() for repository in tag_repositories if repository.GetInfo( 'account' ).HasPermission( HC.GENERAL_ADMIN ) ]
- file_repositories = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.FILE_REPOSITORY ]
+ file_repositories = [ service for service in services if service.GetType() == HC.FILE_REPOSITORY ]
admin_file_service_identifiers = [ repository.GetServiceIdentifier() for repository in file_repositories if repository.GetInfo( 'account' ).HasPermission( HC.GENERAL_ADMIN ) ]
- servers_admin = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.SERVER_ADMIN ]
+ servers_admin = [ service for service in services if service.GetType() == HC.SERVER_ADMIN ]
server_admin_identifiers = [ service.GetServiceIdentifier() for service in servers_admin if service.GetInfo( 'account' ).HasPermission( HC.GENERAL_ADMIN ) ]
if len( admin_tag_service_identifiers ) > 0 or len( admin_file_service_identifiers ) > 0 or len( server_admin_identifiers ) > 0:
@@ -1227,7 +1241,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
def _ModifyAccount( self, service_identifier ):
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
with ClientGUIDialogs.DialogTextEntry( self, 'Enter the access key for the account to be modified.' ) as dlg:
@@ -1313,7 +1327,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
if service_identifier is not None:
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
account = service.GetInfo( 'account' )
@@ -1395,7 +1409,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
news = dlg.GetValue()
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
with wx.BusyCursor(): service.Request( HC.POST, 'news', { 'news' : news } )
@@ -1592,7 +1606,9 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
url_string = url
- message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, url_string )
+ job_key = HC.JobKey( pausable = False, cancellable = False )
+
+ message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, url_string, job_key )
HC.pubsub.pub( 'message', message )
@@ -1620,7 +1636,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def _Stats( self, service_identifier ):
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
response = service.Request( HC.GET, 'stats' )
@@ -2505,7 +2521,7 @@ class FrameReviewServices( ClientGUICommon.Frame ):
def _DisplayAccountInfo( self ):
- self._service = HC.app.Read( 'service', self._service_identifier )
+ self._service = HC.app.GetManager( 'services' ).GetService( self._service_identifier.GetServiceKey() )
service_type = self._service_identifier.GetType()
@@ -2636,7 +2652,7 @@ class FrameReviewServices( ClientGUICommon.Frame ):
if service_type in HC.REPOSITORIES + HC.LOCAL_SERVICES:
- service_info = HC.app.Read( 'service_info', self._service_identifier )
+ service_info = HC.app.Read( 'service_info', self._service_identifier.GetServiceKey() )
if service_type in ( HC.LOCAL_FILE, HC.FILE_REPOSITORY ):
@@ -2781,7 +2797,7 @@ class FrameReviewServices( ClientGUICommon.Frame ):
( name, text, timeout, ( num_hashes, hashes, share_key ) ) = shares[0]
- self._service = HC.app.Read( 'service', HC.LOCAL_BOORU_SERVICE_IDENTIFIER )
+ self._service = HC.app.GetManager( 'services' ).GetService( HC.LOCAL_BOORU_SERVICE_IDENTIFIER.GetServiceKey() )
info = self._service.GetInfo()
@@ -2805,7 +2821,7 @@ class FrameReviewServices( ClientGUICommon.Frame ):
( name, text, timeout, ( num_hashes, hashes, share_key ) ) = shares[0]
- self._service = HC.app.Read( 'service', HC.LOCAL_BOORU_SERVICE_IDENTIFIER )
+ self._service = HC.app.GetManager( 'services' ).GetService( HC.LOCAL_BOORU_SERVICE_IDENTIFIER.GetServiceKey() )
info = self._service.GetInfo()
diff --git a/include/ClientGUICanvas.py b/include/ClientGUICanvas.py
index 30f40bfa..739293c4 100755
--- a/include/ClientGUICanvas.py
+++ b/include/ClientGUICanvas.py
@@ -433,6 +433,19 @@ class Canvas( object ):
self.Bind( wx.EVT_PAINT, self.EventPaint )
+ def _CopyHashToClipboard( self ):
+
+ if wx.TheClipboard.Open():
+
+ data = wx.TextDataObject( self._current_display_media.GetHash().encode( 'hex' ) )
+
+ wx.TheClipboard.SetData( data )
+
+ wx.TheClipboard.Close()
+
+ else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
+
+
def _DrawBackgroundBitmap( self ):
( client_width, client_height ) = self.GetClientSize()
@@ -539,7 +552,7 @@ class Canvas( object ):
if service_identifier in self._service_identifiers_to_services: service = self._service_identifiers_to_services[ service_identifier ]
else:
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
self._service_identifiers_to_services[ service_identifier ] = service
@@ -757,6 +770,8 @@ class Canvas( object ):
if media != self._current_media:
+ HC.app.ResetIdleTimer()
+
with wx.FrozenWindow( self ):
self._current_media = media
@@ -1051,6 +1066,30 @@ class CanvasFullscreenMediaList( ClientGUIMixins.ListeningMediaList, Canvas, Cli
+ def _Remove( self ):
+
+ next_media = self._GetNext( self._current_media )
+
+ if next_media == self._current_media: next_media = None
+
+ hashes = { self._current_display_media.GetHash() }
+
+ HC.pubsub.pub( 'remove_media', self._page_key, hashes )
+
+ singleton_media = { self._current_display_media }
+
+ ClientGUIMixins.ListeningMediaList._RemoveMedia( self, singleton_media, {} )
+
+ if self.HasNoMedia(): self.EventClose( None )
+ elif self.HasMedia( self._current_media ):
+
+ self._DrawBackgroundBitmap()
+
+ self._DrawCurrentMedia()
+
+ else: self.SetMedia( next_media )
+
+
def _ShowFirst( self ): self.SetMedia( self._GetFirst() )
def _ShowLast( self ): self.SetMedia( self._GetLast() )
@@ -1459,6 +1498,7 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaList ):
if command == 'archive': self._Archive()
elif command == 'copy_files':
with wx.BusyCursor(): HC.app.Write( 'copy_files', ( self._current_media.GetHash(), ) )
+ elif command == 'copy_hash': self._CopyHashToClipboard()
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
elif command == 'copy_path': self._CopyPathToClipboard()
elif command == 'delete': self._Delete()
@@ -1481,6 +1521,7 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaList ):
elif command == 'pan_left': self._DoManualPan( -distance, 0 )
elif command == 'pan_right': self._DoManualPan( distance, 0 )
+ elif command == 'remove': self._Remove()
elif command == 'slideshow': self._StartSlideshow( data )
elif command == 'slideshow_pause_play': self._PausePlaySlideshow()
elif command == 'zoom_in': self._ZoomIn()
@@ -1511,9 +1552,9 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaList ):
def EventShowMenu( self, event ):
- services = HC.app.Read( 'services' )
+ services = HC.app.GetManager( 'services' ).GetServices()
- local_ratings_services = [ service for service in services if service.GetServiceIdentifier().GetType() in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) ]
+ local_ratings_services = [ service for service in services if service.GetType() in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) ]
i_can_post_ratings = len( local_ratings_services ) > 0
@@ -1550,25 +1591,33 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaList ):
menu.AppendSeparator()
- menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_tags' ), 'manage tags' )
+ manage_menu = wx.Menu()
- if i_can_post_ratings: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_ratings' ), 'manage ratings' )
+ manage_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_tags' ), 'tags' )
+
+ if i_can_post_ratings: manage_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_ratings' ), 'ratings' )
+
+ menu.AppendMenu( CC.ID_NULL, 'manage', manage_menu )
menu.AppendSeparator()
if self._current_media.HasInbox(): menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'archive' ), '&archive' )
if self._current_media.HasArchive(): menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'inbox' ), 'return to &inbox' )
+ menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'remove', HC.LOCAL_FILE_SERVICE_IDENTIFIER ), '&remove' )
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', HC.LOCAL_FILE_SERVICE_IDENTIFIER ), '&delete' )
- menu.AppendSeparator()
+ share_menu = wx.Menu()
copy_menu = wx.Menu()
copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_files' ) , 'file' )
+ copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_hash' ) , 'hash' )
copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_path' ) , 'path' )
copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_local_url' ) , 'local url' )
- menu.AppendMenu( CC.ID_NULL, 'copy', copy_menu )
+ share_menu.AppendMenu( CC.ID_NULL, 'copy', copy_menu )
+
+ menu.AppendMenu( CC.ID_NULL, 'share', share_menu )
menu.AppendSeparator()
@@ -1582,7 +1631,7 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaList ):
slideshow.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'slideshow', 80 ), 'william gibson' )
slideshow.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'slideshow' ), 'custom interval' )
- menu.AppendMenu( CC.ID_NULL, 'Start Slideshow', slideshow )
+ menu.AppendMenu( CC.ID_NULL, 'start slideshow', slideshow )
if self._timer_slideshow.IsRunning(): menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'slideshow_pause_play' ), 'stop slideshow' )
self._menu_open = True
@@ -1838,6 +1887,7 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaList ):
if command == 'archive': self._Archive()
elif command == 'copy_files':
with wx.BusyCursor(): HC.app.Write( 'copy_files', ( self._current_media.GetHash(), ) )
+ elif command == 'copy_hash': self._CopyHashToClipboard()
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
elif command == 'copy_path': self._CopyPathToClipboard()
elif command == 'delete': self._Delete()
@@ -1851,6 +1901,7 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaList ):
elif command == 'inbox': self._Inbox()
elif command == 'manage_ratings': self._ManageRatings()
elif command == 'manage_tags': self._ManageTags()
+ elif command == 'remove': self._Remove()
elif command == 'slideshow': self._StartSlideshow( data )
elif command == 'slideshow_pause_play': self._PausePlaySlideshow()
elif command == 'zoom_in': self._ZoomIn()
@@ -1914,19 +1965,35 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaList ):
menu.AppendSeparator()
+ manage_menu = wx.Menu()
+
+ manage_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_tags' ), 'tags' )
+
+ if i_can_post_ratings: manage_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'manage_ratings' ), 'ratings' )
+
+ menu.AppendMenu( CC.ID_NULL, 'manage', manage_menu )
+
+ menu.AppendSeparator()
+
if self._current_media.HasInbox(): menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'archive' ), '&archive' )
if self._current_media.HasArchive(): menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'inbox' ), 'return to &inbox' )
+ menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'remove', HC.LOCAL_FILE_SERVICE_IDENTIFIER ), '&remove' )
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', HC.LOCAL_FILE_SERVICE_IDENTIFIER ), '&delete' )
menu.AppendSeparator()
+ share_menu = wx.Menu()
+
copy_menu = wx.Menu()
copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_files' ) , 'file' )
+ copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_hash' ) , 'hash' )
copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_path' ) , 'path' )
copy_menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'copy_local_url' ) , 'local url' )
- menu.AppendMenu( CC.ID_NULL, 'copy', copy_menu )
+ share_menu.AppendMenu( CC.ID_NULL, 'copy', copy_menu )
+
+ menu.AppendMenu( CC.ID_NULL, 'share', share_menu )
menu.AppendSeparator()
@@ -2578,7 +2645,7 @@ class RatingsFilterFrameLike( CanvasFullscreenMediaListFilter ):
CanvasFullscreenMediaListFilter.__init__( self, my_parent, page_key, HC.LOCAL_FILE_SERVICE_IDENTIFIER, [], media_results )
self._rating_service_identifier = service_identifier
- self._service = HC.app.Read( 'service', service_identifier )
+ self._service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
FullscreenPopoutFilterLike( self )
@@ -2651,7 +2718,7 @@ class RatingsFilterFrameNumerical( ClientGUICommon.FrameThatResizes ):
if service_identifier.GetType() == HC.LOCAL_RATING_LIKE: self._score_gap = 1.0
else:
- self._service = HC.app.Read( 'service', service_identifier )
+ self._service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
( self._lower, self._upper ) = self._service.GetLowerUpper()
diff --git a/include/ClientGUICommon.py b/include/ClientGUICommon.py
index 7cf1ae5d..eba498fa 100755
--- a/include/ClientGUICommon.py
+++ b/include/ClientGUICommon.py
@@ -2607,6 +2607,8 @@ class PopupMessageGauge( PopupMessage ):
PopupMessage.__init__( self, parent, message )
+ self._job_key = self._message.GetJobKey()
+
self._done = False
vbox = wx.BoxSizer( wx.VERTICAL )
@@ -2620,11 +2622,16 @@ class PopupMessageGauge( PopupMessage ):
self._gauge = Gauge( self, size = ( 380, -1 ) )
self._gauge.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
+ self._pause_button = wx.Button( self, label = 'pause' )
+ self._pause_button.Bind( wx.EVT_BUTTON, self.EventPauseButton )
+ self._pause_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
+
self._cancel_button = wx.Button( self, label = 'cancel' )
self._cancel_button.Bind( wx.EVT_BUTTON, self.EventCancelButton )
self._cancel_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
hbox.AddF( self._gauge, FLAGS_EXPAND_BOTH_WAYS )
+ hbox.AddF( self._pause_button, FLAGS_MIXED )
hbox.AddF( self._cancel_button, FLAGS_MIXED )
self._show_file_button = wx.Button( self )
@@ -2637,6 +2644,8 @@ class PopupMessageGauge( PopupMessage ):
self.SetSizer( vbox )
+ self._created = HC.GetNow()
+
def Dismiss( self ):
@@ -2653,6 +2662,8 @@ class PopupMessageGauge( PopupMessage ):
+ if self._job_key.IsPaused(): self._job_key.Cancel()
+
PopupMessage.Dismiss( self )
@@ -2660,14 +2671,28 @@ class PopupMessageGauge( PopupMessage ):
if self._message.GetInfo( 'mode' ) == 'cancelable gauge':
- job_key = self._message.GetInfo( 'job_key' )
-
- job_key.Cancel()
+ self._job_key.Cancel()
self._cancel_button.Disable()
+ def EventPauseButton( self, event ):
+
+ if self._job_key.IsPaused():
+
+ self._job_key.Resume()
+
+ self._pause_button.SetLabel( 'pause' )
+
+ else:
+
+ self._job_key.Pause()
+
+ self._pause_button.SetLabel( 'resume' )
+
+
+
def EventShowFileButton( self, event ):
hashes = self._message.GetInfo( 'hashes' )
@@ -2681,6 +2706,9 @@ class PopupMessageGauge( PopupMessage ):
mode = self._message.GetInfo( 'mode' )
text = self._message.GetInfo( 'text' )
+
+ if self._job_key.IsPausable(): self._pause_button.Show()
+ else: self._pause_button.Hide()
if mode == 'files':
@@ -3667,7 +3695,7 @@ class AdvancedTagOptions( AdvancedOptions ):
self._namespaces = namespaces
self._initial_settings = initial_settings
- self._service_identifiers_to_checkbox_info = {}
+ self._service_keys_to_checkbox_info = {}
AdvancedOptions.__init__( self, parent, 'advanced tag options' )
@@ -3678,21 +3706,23 @@ class AdvancedTagOptions( AdvancedOptions ):
self._vbox.Clear( True )
- self._service_identifiers_to_checkbox_info = {}
+ self._service_keys_to_checkbox_info = {}
- service_identifiers = HC.app.Read( 'service_identifiers', ( HC.TAG_REPOSITORY, HC.LOCAL_TAG ) )
+ services = HC.app.GetManager( 'services' ).GetServices( ( HC.TAG_REPOSITORY, HC.LOCAL_TAG ) )
- if len( service_identifiers ) > 0:
+ if len( services ) > 0:
outer_gridbox = wx.FlexGridSizer( 0, 2 )
outer_gridbox.AddGrowableCol( 1, 1 )
- for service_identifier in service_identifiers:
+ for service in services:
- self._service_identifiers_to_checkbox_info[ service_identifier ] = []
+ service_key = service.GetKey()
- outer_gridbox.AddF( wx.StaticText( panel, label = service_identifier.GetName() ), FLAGS_MIXED )
+ self._service_keys_to_checkbox_info[ service_key ] = []
+
+ outer_gridbox.AddF( wx.StaticText( panel, label = service.GetName() ), FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
@@ -3703,12 +3733,12 @@ class AdvancedTagOptions( AdvancedOptions ):
namespace_checkbox = wx.CheckBox( panel, label = label )
- if service_identifier in self._initial_settings and namespace in self._initial_settings[ service_identifier ]: namespace_checkbox.SetValue( True )
+ if service_key in self._initial_settings and namespace in self._initial_settings[ service_key ]: namespace_checkbox.SetValue( True )
else: namespace_checkbox.SetValue( False )
namespace_checkbox.Bind( wx.EVT_CHECKBOX, self.EventChecked )
- self._service_identifiers_to_checkbox_info[ service_identifier ].append( ( namespace, namespace_checkbox ) )
+ self._service_keys_to_checkbox_info[ service_key ].append( ( namespace, namespace_checkbox ) )
vbox.AddF( namespace_checkbox, FLAGS_EXPAND_BOTH_WAYS )
@@ -3746,11 +3776,11 @@ class AdvancedTagOptions( AdvancedOptions ):
result = {}
- for ( service_identifier, checkbox_info ) in self._service_identifiers_to_checkbox_info.items():
+ for ( service_key, checkbox_info ) in self._service_keys_to_checkbox_info.items():
namespaces = [ namespace for ( namespace, checkbox ) in checkbox_info if checkbox.GetValue() == True ]
- result[ service_identifier ] = namespaces
+ result[ service_key ] = namespaces
return result
@@ -3767,13 +3797,13 @@ class AdvancedTagOptions( AdvancedOptions ):
def SetInfo( self, info ):
- for ( service_identifier, checkbox_info ) in self._service_identifiers_to_checkbox_info.items():
+ for ( service_key, checkbox_info ) in self._service_keys_to_checkbox_info.items():
- if service_identifier in info:
+ if service_key in info:
for ( namespace, checkbox ) in checkbox_info:
- if namespace in info[ service_identifier ]: checkbox.SetValue( True )
+ if namespace in info[ service_key ]: checkbox.SetValue( True )
else: checkbox.SetValue( False )
diff --git a/include/ClientGUIDialogs.py b/include/ClientGUIDialogs.py
index 41ce8e7f..19265bac 100755
--- a/include/ClientGUIDialogs.py
+++ b/include/ClientGUIDialogs.py
@@ -63,7 +63,7 @@ def SelectServiceIdentifier( permission = None, service_types = HC.ALL_SERVICES,
if service_identifiers is None:
- services = HC.app.Read( 'services', service_types )
+ services = HC.app.GetManager( 'services' ).GetServices( service_types )
if permission is not None: services = [ service for service in services if service.GetInfo( 'account' ).HasPermission( permission ) ]
@@ -608,7 +608,7 @@ class DialogGenerateNewAccounts( Dialog ):
self._num.SetValue( 1 )
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
response = service.Request( HC.GET, 'account_types' )
@@ -670,7 +670,7 @@ class DialogGenerateNewAccounts( Dialog ):
lifetime = self._lifetime.GetClientData( self._lifetime.GetSelection() )
- service = HC.app.Read( 'service', self._service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( self._service_identifier.GetServiceKey() )
try:
@@ -894,7 +894,7 @@ class DialogInputCustomFilterAction( Dialog ):
service_identifier = self._ratings_like_service_identifiers.GetClientData( selection )
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
self._current_ratings_like_service = service
@@ -918,7 +918,7 @@ class DialogInputCustomFilterAction( Dialog ):
service_identifier = self._ratings_numerical_service_identifiers.GetClientData( selection )
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
self._current_ratings_numerical_service = service
@@ -1535,8 +1535,8 @@ class DialogInputFileSystemPredicate( Dialog ):
def PopulateControls():
- self._local_numericals = HC.app.Read( 'services', ( HC.LOCAL_RATING_NUMERICAL, ) )
- self._local_likes = HC.app.Read( 'services', ( HC.LOCAL_RATING_LIKE, ) )
+ self._local_numericals = HC.app.GetManager( 'services' ).GetServices( ( HC.LOCAL_RATING_NUMERICAL, ) )
+ self._local_likes = HC.app.GetManager( 'services' ).GetServices( ( HC.LOCAL_RATING_LIKE, ) )
for service in self._local_numericals: self._service_numerical.Append( service.GetServiceIdentifier().GetName(), service )
@@ -2108,7 +2108,7 @@ class DialogInputLocalBooruShare( Dialog ):
def EventCopyExternalShareURL( self, event ):
- self._service = HC.app.Read( 'service', HC.LOCAL_BOORU_SERVICE_IDENTIFIER )
+ self._service = HC.app.GetManager( 'services' ).GetService( HC.LOCAL_BOORU_SERVICE_IDENTIFIER.GetServiceKey() )
info = self._service.GetInfo()
@@ -2125,7 +2125,7 @@ class DialogInputLocalBooruShare( Dialog ):
def EventCopyInternalShareURL( self, event ):
- self._service = HC.app.Read( 'service', HC.LOCAL_BOORU_SERVICE_IDENTIFIER )
+ self._service = HC.app.GetManager( 'services' ).GetService( HC.LOCAL_BOORU_SERVICE_IDENTIFIER.GetServiceKey() )
info = self._service.GetInfo()
@@ -3550,7 +3550,7 @@ class DialogModifyAccounts( Dialog ):
Dialog.__init__( self, parent, 'modify account' )
- self._service = HC.app.Read( 'service', service_identifier )
+ self._service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
self._subject_identifiers = list( subject_identifiers )
InitialiseControls()
@@ -3766,9 +3766,9 @@ class DialogPageChooser( Dialog ):
ArrangeControls()
- self._services = HC.app.Read( 'services' )
+ self._services = HC.app.GetManager( 'services' ).GetServices()
- self._petition_service_identifiers = [ service.GetServiceIdentifier() for service in self._services if service.GetServiceIdentifier().GetType() in HC.REPOSITORIES and service.GetInfo( 'account' ).HasPermission( HC.RESOLVE_PETITIONS ) ]
+ self._petition_service_identifiers = [ service.GetServiceIdentifier() for service in self._services if service.GetType() in HC.REPOSITORIES and service.GetInfo( 'account' ).HasPermission( HC.RESOLVE_PETITIONS ) ]
self._InitButtons( 'home' )
@@ -3975,7 +3975,7 @@ class DialogPathsToTagsRegex( Dialog ):
def PopulateControls():
- services = HC.app.Read( 'services', ( HC.TAG_REPOSITORY, ) )
+ services = HC.app.GetManager( 'services' ).GetServices( ( HC.TAG_REPOSITORY, ) )
for service in services:
@@ -3999,7 +3999,9 @@ class DialogPathsToTagsRegex( Dialog ):
self._tag_repositories.AddPage( page, name )
- default_tag_repository = HC.options[ 'default_tag_repository' ]
+ default_tag_repository_key = HC.options[ 'default_tag_repository' ]
+
+ default_tag_repository = HC.app.GetManager( 'services' ).GetService( default_tag_repository_key )
self._tag_repositories.Select( default_tag_repository.GetName() )
@@ -4890,6 +4892,8 @@ class DialogSelectYoutubeURL( Dialog ):
url_string = title + ' ' + resolution + ' ' + extension
+ job_key = HC.JobKey( pausable = False, cancellable = False )
+
message = HC.MessageGauge( HC.MESSAGE_TYPE_GAUGE, url_string )
HydrusThreading.CallToThread( HydrusDownloading.THREADDownloadURL, message, url, url_string )
diff --git a/include/ClientGUIDialogsManage.py b/include/ClientGUIDialogsManage.py
index 8c70e042..a5654e8b 100644
--- a/include/ClientGUIDialogsManage.py
+++ b/include/ClientGUIDialogsManage.py
@@ -209,7 +209,7 @@ class DialogManageAccountTypes( ClientGUIDialogs.Dialog ):
def PopulateControls():
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
response = service.Request( HC.GET, 'account_types' )
@@ -383,7 +383,7 @@ class DialogManageAccountTypes( ClientGUIDialogs.Dialog ):
def EventOK( self, event ):
- service = HC.app.Read( 'service', self._service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( self._service_identifier.GetServiceKey() )
service.Request( HC.POST, 'account_types', { 'edit_log' : self._edit_log } )
@@ -2845,7 +2845,7 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
self._default_tag_sort.Append( 'incidence (desc)', CC.SORT_BY_INCIDENCE_DESC )
self._default_tag_sort.Append( 'incidence (asc)', CC.SORT_BY_INCIDENCE_ASC )
- self._default_tag_repository = wx.Choice( self._gui_page )
+ self._default_tag_repository = ClientGUICommon.BetterChoice( self._gui_page )
self._fullscreen_borderless = wx.CheckBox( self._gui_page )
@@ -3050,11 +3050,13 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_DESC: self._default_tag_sort.Select( 2 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_ASC: self._default_tag_sort.Select( 3 )
- service_identifiers = HC.app.Read( 'service_identifiers', ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ) )
+ services = HC.app.GetManager( 'services' ).GetServices( ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ) )
- for service_identifier in service_identifiers: self._default_tag_repository.Append( service_identifier.GetName(), service_identifier )
+ for service in services: self._default_tag_repository.Append( service.GetName(), service.GetKey() )
- self._default_tag_repository.SetStringSelection( HC.options[ 'default_tag_repository' ].GetName() )
+ default_tag_repository_key = HC.options[ 'default_tag_repository' ]
+
+ self._default_tag_repository.SelectClientData( default_tag_repository_key )
self._fullscreen_borderless.SetValue( HC.options[ 'fullscreen_borderless' ] )
@@ -3644,7 +3646,7 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
HC.options[ 'shortcuts' ] = shortcuts
- HC.options[ 'default_tag_repository' ] = self._default_tag_repository.GetClientData( self._default_tag_repository.GetSelection() )
+ HC.options[ 'default_tag_repository' ] = self._default_tag_repository.GetChoice()
HC.options[ 'default_tag_sort' ] = self._default_tag_sort.GetClientData( self._default_tag_sort.GetSelection() )
new_local_port = self._local_port.GetValue()
@@ -3948,7 +3950,7 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
wx.Panel.__init__( self, parent )
self._service_identifier = service_identifier
- self._service = HC.app.Read( 'service', service_identifier )
+ self._service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
self._media = media
@@ -4290,7 +4292,7 @@ class DialogManageServer( ClientGUIDialogs.Dialog ):
self._service_identifier = service_identifier
- self._service = HC.app.Read( 'service', self._service_identifier )
+ self._service = HC.app.GetManager( 'services' ).GetService( self._service_identifier.GetServiceKey() )
InitialiseControls()
@@ -4606,7 +4608,7 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
parent_listbook.AddPage( listbook, name )
- services = HC.app.Read( 'services', manageable_service_types )
+ services = HC.app.GetManager( 'services' ).GetServices( manageable_service_types )
for service in services:
@@ -5960,9 +5962,11 @@ class DialogManageTagCensorship( ClientGUIDialogs.Dialog ):
self._tag_services.AddPage( page, name )
- default_tag_repository = HC.options[ 'default_tag_repository' ]
+ default_tag_repository_key = HC.options[ 'default_tag_repository' ]
- self._tag_services.Select( default_tag_repository.GetName() )
+ service = HC.app.GetManager( 'services' ).GetService( default_tag_repository_key )
+
+ self._tag_services.Select( service.GetName() )
def ArrangeControls():
@@ -6130,7 +6134,7 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
def PopulateControls():
- services = HC.app.Read( 'services', ( HC.TAG_REPOSITORY, ) )
+ services = HC.app.GetManager( 'services' ).GetServices( ( HC.TAG_REPOSITORY, ) )
for service in services:
@@ -6154,9 +6158,11 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
self._tag_repositories.AddPage( page, name )
- default_tag_repository = HC.options[ 'default_tag_repository' ]
+ default_tag_repository_key = HC.options[ 'default_tag_repository' ]
- self._tag_repositories.Select( default_tag_repository.GetName() )
+ service = HC.app.GetManager( 'services' ).GetService( default_tag_repository_key )
+
+ self._tag_repositories.Select( service.GetName() )
def ArrangeControls():
@@ -6309,7 +6315,7 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
self._account = service.GetInfo( 'account' )
@@ -6598,7 +6604,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
self._tag_repositories.AddPage( page, name )
- services = HC.app.Read( 'services', ( HC.TAG_REPOSITORY, ) )
+ services = HC.app.GetManager( 'services' ).GetServices( ( HC.TAG_REPOSITORY, ) )
for service in services:
@@ -6616,9 +6622,11 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
- default_tag_repository = HC.options[ 'default_tag_repository' ]
+ default_tag_repository_key = HC.options[ 'default_tag_repository' ]
- self._tag_repositories.Select( default_tag_repository.GetName() )
+ service = HC.app.GetManager( 'services' ).GetService( default_tag_repository_key )
+
+ self._tag_repositories.Select( service.GetName() )
def ArrangeControls():
@@ -6777,7 +6785,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
self._account = service.GetInfo( 'account' )
@@ -7273,9 +7281,11 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
self._tag_repositories.AddPage( page_info, name )
- default_tag_repository = HC.options[ 'default_tag_repository' ]
+ default_tag_repository_key = HC.options[ 'default_tag_repository' ]
- self._tag_repositories.Select( default_tag_repository.GetName() )
+ service = HC.app.GetManager( 'services' ).GetService( default_tag_repository_key )
+
+ self._tag_repositories.Select( service.GetName() )
def ArrangeControls():
@@ -7449,7 +7459,7 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
if not self._i_am_local_tag_service:
- service = HC.app.Read( 'service', tag_service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( tag_service_identifier.GetServiceKey() )
self._account = service.GetInfo( 'account' )
diff --git a/include/ClientGUIManagement.py b/include/ClientGUIManagement.py
index ab4600c9..f9fcc5d5 100755
--- a/include/ClientGUIManagement.py
+++ b/include/ClientGUIManagement.py
@@ -639,10 +639,15 @@ class ManagementPanelDumper( ManagementPanel ):
advanced_tag_options = self._advanced_tag_options.GetInfo()
- for ( service_identifier, namespaces ) in advanced_tag_options.items():
+ for ( service_key, namespaces ) in advanced_tag_options.items():
tags_manager = media.GetTagsManager()
+ try: service = HC.app.GetManager( 'services' ).GetService( service_key )
+ except: continue
+
+ service_identifier = service.GetKey()
+
current = tags_manager.GetCurrent( service_identifier )
pending = tags_manager.GetPending( service_identifier )
@@ -1820,7 +1825,7 @@ class ManagementPanelPetitions( ManagementPanel ):
ManagementPanel.__init__( self, parent, page, page_key, file_service_identifier, starting_from_session = starting_from_session )
- self._service = HC.app.Read( 'service', self._petition_service_identifier )
+ self._service = HC.app.GetManager( 'services' ).GetService( self._petition_service_identifier.GetServiceKey() )
self._can_ban = self._service.GetInfo( 'account' ).HasPermission( HC.MANAGE_USERS )
self._num_petitions = None
diff --git a/include/ClientGUIMedia.py b/include/ClientGUIMedia.py
index 7cc6338a..64872d17 100755
--- a/include/ClientGUIMedia.py
+++ b/include/ClientGUIMedia.py
@@ -91,6 +91,7 @@ class MediaPanel( ClientGUIMixins.ListeningMediaList, wx.ScrolledWindow ):
HC.pubsub.sub( self, 'Collect', 'collect_media' )
HC.pubsub.sub( self, 'Sort', 'sort_media' )
HC.pubsub.sub( self, 'FileDumped', 'file_dumped' )
+ HC.pubsub.sub( self, 'RemoveMedia', 'remove_media' )
self._PublishSelectionChange()
@@ -815,6 +816,16 @@ class MediaPanel( ClientGUIMixins.ListeningMediaList, wx.ScrolledWindow ):
+ def RemoveMedia( self, page_key, hashes ):
+
+ if page_key == self._page_key:
+
+ media = self._GetMedia( hashes )
+
+ self._RemoveMedia( media, {} )
+
+
+
def SetFocussedMedia( self, page_key, media ):
if page_key == self._page_key:
@@ -1678,13 +1689,13 @@ class MediaPanelThumbnails( MediaPanel ):
multiple_selected = num_selected > 1
- services = HC.app.Read( 'services' )
+ services = HC.app.GetManager( 'services' ).GetServices()
- tag_repositories = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.TAG_REPOSITORY ]
+ tag_repositories = [ service for service in services if service.GetType() == HC.TAG_REPOSITORY ]
- file_repositories = [ service for service in services if service.GetServiceIdentifier().GetType() == HC.FILE_REPOSITORY ]
+ file_repositories = [ service for service in services if service.GetType() == HC.FILE_REPOSITORY ]
- local_ratings_services = [ service for service in services if service.GetServiceIdentifier().GetType() in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) ]
+ local_ratings_services = [ service for service in services if service.GetType() in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) ]
i_can_post_ratings = len( local_ratings_services ) > 0
diff --git a/include/ClientGUIMessages.py b/include/ClientGUIMessages.py
index 3411a7da..272e0dde 100755
--- a/include/ClientGUIMessages.py
+++ b/include/ClientGUIMessages.py
@@ -626,7 +626,7 @@ class DestinationPanel( wx.Panel ):
elif command == 'read': status = 'read'
elif command == 'unread': status = 'sent'
- my_message_depot = HC.app.Read( 'service', self._identity )
+ my_message_depot = HC.app.GetManager( 'services' ).GetService( self._identity.GetServiceKey() )
connection = my_message_depot.GetConnection()
@@ -1250,7 +1250,7 @@ class DraftPanel( wx.Panel ):
try:
- my_message_depot = HC.app.Read( 'service', self._contact_from )
+ my_message_depot = HC.app.GetManager( 'services' ).GetService( self._contact_from.GetServiceKey() )
connection = my_message_depot.GetConnection()
diff --git a/include/HydrusConstants.py b/include/HydrusConstants.py
index 874f2e1e..c5c4fa43 100755
--- a/include/HydrusConstants.py
+++ b/include/HydrusConstants.py
@@ -64,7 +64,7 @@ options = {}
# Misc
NETWORK_VERSION = 13
-SOFTWARE_VERSION = 125
+SOFTWARE_VERSION = 126
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
@@ -1522,12 +1522,17 @@ class ClientServiceIdentifier( HydrusYAMLBase ):
def GetType( self ): return self._type
-LOCAL_FILE_SERVICE_IDENTIFIER = ClientServiceIdentifier( 'local files', LOCAL_FILE, 'local files' )
-LOCAL_TAG_SERVICE_IDENTIFIER = ClientServiceIdentifier( 'local tags', LOCAL_TAG, 'local tags' )
-LOCAL_BOORU_SERVICE_IDENTIFIER = ClientServiceIdentifier( 'local booru', LOCAL_BOORU, 'local booru' )
-COMBINED_FILE_SERVICE_IDENTIFIER = ClientServiceIdentifier( 'all known files', COMBINED_FILE, 'all known files' )
-COMBINED_TAG_SERVICE_IDENTIFIER = ClientServiceIdentifier( 'all known tags', COMBINED_TAG, 'all known tags' )
-NULL_SERVICE_IDENTIFIER = ClientServiceIdentifier( '', NULL_SERVICE, 'no service' )
+LOCAL_FILE_SERVICE_KEY = 'local files'
+LOCAL_TAG_SERVICE_KEY = 'local tags'
+LOCAL_BOORU_SERVICE_KEY = 'local booru'
+COMBINED_FILE_SERVICE_KEY = 'all known files'
+COMBINED_TAG_SERVICE_KEY = 'all known tags'
+
+LOCAL_FILE_SERVICE_IDENTIFIER = ClientServiceIdentifier( LOCAL_FILE_SERVICE_KEY, LOCAL_FILE, 'local files' )
+LOCAL_TAG_SERVICE_IDENTIFIER = ClientServiceIdentifier( LOCAL_TAG_SERVICE_KEY, LOCAL_TAG, 'local tags' )
+LOCAL_BOORU_SERVICE_IDENTIFIER = ClientServiceIdentifier( LOCAL_BOORU_SERVICE_KEY, LOCAL_BOORU, 'local booru' )
+COMBINED_FILE_SERVICE_IDENTIFIER = ClientServiceIdentifier( COMBINED_FILE_SERVICE_KEY, COMBINED_FILE, 'all known files' )
+COMBINED_TAG_SERVICE_IDENTIFIER = ClientServiceIdentifier( COMBINED_TAG_SERVICE_KEY, COMBINED_TAG, 'all known tags' )
class ClientToServerUpdate( HydrusYAMLBase ):
@@ -1742,10 +1747,13 @@ class JobDatabase( object ):
class JobKey( object ):
- def __init__( self ):
+ def __init__( self, pausable = True, cancellable = True ):
self._key = os.urandom( 32 )
+ self._pausable = pausable
+ self._cancellable = cancellable
+
self._begun = threading.Event()
self._done = threading.Event()
self._cancelled = threading.Event()
@@ -1770,6 +1778,11 @@ class JobKey( object ):
self.Finish()
+ def DeleteVariable( self, name ):
+
+ with self._variable_lock: del self._variables[ name ]
+
+
def Finish( self ): self._done.set()
def GetKey( self ): return self._key
@@ -1786,10 +1799,14 @@ class JobKey( object ):
def IsBegun( self ): return self._begun.is_set()
+ def IsCancellable( self ): return self._cancellable
+
def IsCancelled( self ): return shutdown or self._cancelled.is_set()
def IsDone( self ): return shutdown or self._done.is_set()
+ def IsPausable( self ): return self._pausable
+
def IsPaused( self ): return self._paused.is_set()
def IsWorking( self ): return self.IsBegun() and not self.IsDone()
@@ -1804,6 +1821,10 @@ class JobKey( object ):
def Resume( self ): self._paused.clear()
+ def SetCancellable( self, value ): self._cancellable = value
+
+ def SetPausable( self, value ): self._pausable = value
+
def SetVariable( self, name, value ):
with self._variable_lock: self._variables[ name ] = value
@@ -1906,16 +1927,18 @@ class Message( object ):
class MessageGauge( Message ):
- def __init__( self, message_type, text ):
+ def __init__( self, message_type, text, job_key ):
- info = {}
+ Message.__init__( self, message_type, {} )
- info[ 'mode' ] = 'text'
- info[ 'text' ] = text
+ self._info[ 'mode' ] = 'text'
+ self._info[ 'text' ] = text
- Message.__init__( self, message_type, info )
+ self._job_key = job_key
+ def GetJobKey( self ): return self._job_key
+
class Predicate( HydrusYAMLBase ):
yaml_tag = u'!Predicate'
@@ -2224,7 +2247,8 @@ class ServerServiceIdentifier( HydrusYAMLBase ):
def GetType( self ): return self._type
-SERVER_ADMIN_IDENTIFIER = ServerServiceIdentifier( 'server admin', SERVER_ADMIN )
+SERVER_ADMIN_KEY = 'server admin'
+SERVER_ADMIN_IDENTIFIER = ServerServiceIdentifier( SERVER_ADMIN_KEY, SERVER_ADMIN )
class ServiceUpdate( object ):
diff --git a/include/HydrusDownloading.py b/include/HydrusDownloading.py
index 11824c90..ce7cc31c 100644
--- a/include/HydrusDownloading.py
+++ b/include/HydrusDownloading.py
@@ -56,7 +56,10 @@ def ConvertTagsToServiceIdentifiersToTags( tags, advanced_tag_options ):
siblings_manager = HC.app.GetManager( 'tag_siblings' )
parents_manager = HC.app.GetManager( 'tag_parents' )
- for ( service_identifier, namespaces ) in advanced_tag_options.items():
+ for ( service_key, namespaces ) in advanced_tag_options.items():
+
+ try: service_identifier = HC.app.GetManager( 'services' ).GetService( service_key ).GetServiceIdentifier()
+ except: continue
if len( namespaces ) > 0:
@@ -1766,6 +1769,7 @@ def THREADDownloadURL( message, url, url_string ):
message.SetInfo( 'range', None )
message.SetInfo( 'value', None )
message.SetInfo( 'mode', 'gauge' )
+
temp_path = HC.http.Request( HC.GET, url, response_to_path = True, report_hooks = [ hook ] )
message.SetInfo( 'range', None )
diff --git a/include/HydrusSessions.py b/include/HydrusSessions.py
index 09d564a4..5563c286 100755
--- a/include/HydrusSessions.py
+++ b/include/HydrusSessions.py
@@ -120,7 +120,7 @@ class HydrusSessionManagerClient( object ):
# session key expired or not found
- service = HC.app.Read( 'service', service_identifier )
+ service = HC.app.GetManager( 'services' ).GetService( service_identifier.GetServiceKey() )
( response, cookies ) = service.Request( HC.GET, 'session_key', return_cookies = True )
diff --git a/include/ServerDB.py b/include/ServerDB.py
index b90d208f..4e8dbb6e 100755
--- a/include/ServerDB.py
+++ b/include/ServerDB.py
@@ -2884,9 +2884,9 @@ def DAEMONUPnP():
service_identifiers = HC.app.ReadDaemon( 'service_identifiers' )
- all_options = { service_identifier : HC.app.ReadDaemon( 'options', service_identifier ) for service_identifier in service_identifiers }
+ all_infos = HC.app.ReadDaemon( 'services' )
- for ( service_identifier, options ) in all_options.items():
+ for ( service_identifier, options ) in all_infos:
internal_port = options[ 'port' ]
upnp = options[ 'upnp' ]
@@ -2899,12 +2899,12 @@ def DAEMONUPnP():
- for ( service_identifier, options ) in all_options.items():
+ for ( service_identifier, options ) in all_infos:
internal_port = options[ 'port' ]
upnp = options[ 'upnp' ]
- if ( local_ip, internal_port ) not in our_mappings:
+ if upnp is not None and ( local_ip, internal_port ) not in our_mappings:
external_port = upnp
diff --git a/include/TestDB.py b/include/TestDB.py
index e55e68b8..11dac85e 100644
--- a/include/TestDB.py
+++ b/include/TestDB.py
@@ -961,14 +961,6 @@ class TestClientDB( unittest.TestCase ):
#
- result = self._read( 'service', HC.LOCAL_FILE_SERVICE_IDENTIFIER )
-
- self.assertEqual( result.GetServiceIdentifier(), HC.LOCAL_FILE_SERVICE_IDENTIFIER )
-
- result = self._read( 'service', HC.LOCAL_TAG_SERVICE_IDENTIFIER )
-
- self.assertEqual( result.GetServiceIdentifier(), HC.LOCAL_TAG_SERVICE_IDENTIFIER )
-
result = self._read( 'services', ( HC.LOCAL_FILE, HC.LOCAL_TAG ) )
result_s_is = { service.GetServiceIdentifier() for service in result }
@@ -977,7 +969,7 @@ class TestClientDB( unittest.TestCase ):
#
- result = self._read( 'service_info', HC.LOCAL_FILE_SERVICE_IDENTIFIER )
+ result = self._read( 'service_info', HC.LOCAL_FILE_SERVICE_IDENTIFIER.GetServiceKey() )
self.assertEqual( type( result ), dict )
@@ -1055,18 +1047,7 @@ class TestClientDB( unittest.TestCase ):
self._write( 'update_services', edit_log )
- with self.assertRaises( HydrusExceptions.DBException ):
-
- self._read( 'service', new_local_like )
-
-
- result = self._read( 'service', other_new_tag_repo )
-
- host = other_new_tag_repo_info_updated[ 'host' ]
- port = other_new_tag_repo_info_updated[ 'port' ]
- access_key = other_new_tag_repo_info_updated[ 'access_key' ]
-
- self.assertEqual( result.GetCredentials(), CC.Credentials( host, port, access_key ) )
+ # update this ~sometime~ to test the new services manager object, which should update with these changes!
#
@@ -1076,10 +1057,7 @@ class TestClientDB( unittest.TestCase ):
self._write( 'update_services', edit_log )
- with self.assertRaises( HydrusExceptions.DBException ):
-
- self._read( 'service', other_new_tag_repo )
-
+ # update this ~sometime~ to test the new services manager object, which should update with these changes!
#
diff --git a/include/TestFunctions.py b/include/TestFunctions.py
index c037e7e2..cd13d9b7 100644
--- a/include/TestFunctions.py
+++ b/include/TestFunctions.py
@@ -58,8 +58,8 @@ class TestHydrusDownloadingFunctions( unittest.TestCase ):
advanced_tag_options = {}
- advanced_tag_options[ local ] = [ '', 'character' ]
- advanced_tag_options[ remote ] = [ '', 'character' ]
+ advanced_tag_options[ local.GetServiceKey() ] = [ '', 'character' ]
+ advanced_tag_options[ remote.GetServiceKey() ] = [ '', 'character' ]
tags = [ 'a', 'character:b', 'series:c' ]
diff --git a/test.py b/test.py
index a71408d5..7d5f5f82 100644
--- a/test.py
+++ b/test.py
@@ -52,6 +52,11 @@ class App( wx.App ):
self._reads[ 'messaging_sessions' ] = []
self._reads[ 'tag_censorship' ] = []
self._reads[ 'options' ] = CC.CLIENT_DEFAULT_OPTIONS
+
+ services = []
+ services.append( CC.Service( HC.LOCAL_BOORU_SERVICE_KEY, HC.LOCAL_BOORU, 'local booru', {} ) )
+ self._reads[ 'services' ] = services
+
self._reads[ 'sessions' ] = []
self._reads[ 'tag_parents' ] = {}
self._reads[ 'tag_service_precedence' ] = []
@@ -65,6 +70,8 @@ class App( wx.App ):
self._managers = {}
+ self._managers[ 'services' ] = CC.ServiceManager()
+
self._managers[ 'hydrus_sessions' ] = HydrusSessions.HydrusSessionManagerClient()
self._managers[ 'tag_censorship' ] = HydrusTags.TagCensorshipManager()
self._managers[ 'tag_siblings' ] = HydrusTags.TagSiblingsManager()