hydrus/include/ClientData.py

2747 lines
98 KiB
Python

import ClientConstants as CC
import ClientDefaults
import ClientDownloading
import ClientFiles
import ClientNetworking
import ClientThreading
import collections
import datetime
import HydrusConstants as HC
import HydrusExceptions
import HydrusFileHandling
import HydrusPaths
import HydrusSerialisable
import HydrusTags
import threading
import traceback
import os
import random
import requests
import shutil
import sqlite3
import stat
import sys
import time
import urllib
import wx
import yaml
import HydrusData
import HydrusGlobals
import HydrusThreading
def AddPaddingToDimensions( dimensions, padding ):
( x, y ) = dimensions
return ( x + padding, y + padding )
def CatchExceptionClient( etype, value, tb ):
try:
trace_list = traceback.format_tb( tb )
trace = ''.join( trace_list )
pretty_value = HydrusData.ToUnicode( value )
if os.linesep in pretty_value:
( first_line, anything_else ) = pretty_value.split( os.linesep, 1 )
trace = trace + os.linesep + anything_else
else:
first_line = pretty_value
job_key = ClientThreading.JobKey()
if etype == HydrusExceptions.ShutdownException:
return
else:
if etype == wx.PyDeadObjectError:
HydrusData.Print( 'Got a PyDeadObjectError, which can probably be ignored, but here it is anyway:' )
HydrusData.Print( trace )
return
try: job_key.SetVariable( 'popup_title', HydrusData.ToUnicode( etype.__name__ ) )
except: job_key.SetVariable( 'popup_title', HydrusData.ToUnicode( etype ) )
job_key.SetVariable( 'popup_text_1', first_line )
job_key.SetVariable( 'popup_traceback', trace )
text = job_key.ToString()
HydrusData.Print( 'Uncaught exception:' )
HydrusData.DebugPrint( text )
HydrusGlobals.client_controller.pub( 'message', job_key )
except:
text = 'Encountered an error I could not parse:'
text += os.linesep
text += HydrusData.ToUnicode( ( etype, value, tb ) )
try: text += traceback.format_exc()
except: pass
HydrusData.ShowText( text )
time.sleep( 1 )
def ConvertServiceKeysToContentUpdatesToPrettyString( service_keys_to_content_updates ):
num_files = 0
actions = set()
locations = set()
extra_words = ''
for ( service_key, content_updates ) in service_keys_to_content_updates.items():
if len( content_updates ) > 0:
name = HydrusGlobals.client_controller.GetServicesManager().GetService( service_key ).GetName()
locations.add( name )
for content_update in content_updates:
( data_type, action, row ) = content_update.ToTuple()
if data_type == HC.CONTENT_TYPE_MAPPINGS: extra_words = ' tags for'
actions.add( HC.content_update_string_lookup[ action ] )
num_files += len( content_update.GetHashes() )
s = ', '.join( locations ) + '->' + ', '.join( actions ) + extra_words + ' ' + HydrusData.ConvertIntToPrettyString( num_files ) + ' files'
return s
def ConvertServiceKeysToTagsToServiceKeysToContentUpdates( hashes, service_keys_to_tags ):
service_keys_to_content_updates = {}
for ( service_key, tags ) in service_keys_to_tags.items():
if service_key == CC.LOCAL_TAG_SERVICE_KEY: action = HC.CONTENT_UPDATE_ADD
else: action = HC.CONTENT_UPDATE_PEND
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, action, ( tag, hashes ) ) for tag in tags ]
service_keys_to_content_updates[ service_key ] = content_updates
return service_keys_to_content_updates
def ConvertShortcutToPrettyShortcut( modifier, key ):
if modifier == wx.ACCEL_NORMAL: modifier = ''
elif modifier == wx.ACCEL_ALT: modifier = 'alt'
elif modifier == wx.ACCEL_CTRL: modifier = 'ctrl'
elif modifier == wx.ACCEL_SHIFT: modifier = 'shift'
if key in range( 65, 91 ): key = chr( key + 32 ) # + 32 for converting ascii A -> a
elif key in range( 97, 123 ): key = chr( key )
else: key = CC.wxk_code_string_lookup[ key ]
return ( modifier, key )
def ConvertZoomToPercentage( zoom ):
zoom_percent = zoom * 100
pretty_zoom = '%.2f' % zoom_percent + '%'
if pretty_zoom.endswith( '00%' ):
pretty_zoom = '%i' % zoom_percent + '%'
return pretty_zoom
def DeletePath( path ):
if HC.options[ 'delete_to_recycle_bin' ] == True:
HydrusPaths.RecyclePath( path )
else:
HydrusPaths.DeletePath( path )
def GenerateService( service_key, service_type, name, info ):
if 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, info )
def GetMediasTagCount( pool, tag_service_key = CC.COMBINED_TAG_SERVICE_KEY, collapse_siblings = False ):
siblings_manager = HydrusGlobals.client_controller.GetManager( 'tag_siblings' )
tags_managers = []
for media in pool:
if media.IsCollection():
tags_managers.extend( media.GetSingletonsTagsManagers() )
else:
tags_managers.append( media.GetTagsManager() )
current_tags_to_count = collections.Counter()
deleted_tags_to_count = collections.Counter()
pending_tags_to_count = collections.Counter()
petitioned_tags_to_count = collections.Counter()
for tags_manager in tags_managers:
statuses_to_tags = tags_manager.GetStatusesToTags( tag_service_key )
# combined is already collapsed
if tag_service_key != CC.COMBINED_TAG_SERVICE_KEY and collapse_siblings:
statuses_to_tags = siblings_manager.CollapseStatusesToTags( tag_service_key, statuses_to_tags )
current_tags_to_count.update( statuses_to_tags[ HC.CURRENT ] )
deleted_tags_to_count.update( statuses_to_tags[ HC.DELETED ] )
pending_tags_to_count.update( statuses_to_tags[ HC.PENDING ] )
petitioned_tags_to_count.update( statuses_to_tags[ HC.PETITIONED ] )
return ( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count )
def GetSortChoices( add_namespaces_and_ratings = True ):
sort_choices = list( CC.SORT_CHOICES )
if add_namespaces_and_ratings:
sort_choices.extend( HC.options[ 'sort_by' ] )
ratings_services = HydrusGlobals.client_controller.GetServicesManager().GetServices( ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) )
for ratings_service in ratings_services:
sort_choices.append( ( 'rating_descend', ratings_service ) )
sort_choices.append( ( 'rating_ascend', ratings_service ) )
return sort_choices
def MergeCounts( min_a, max_a, min_b, max_b ):
# 100-None and 100-None returns 100-200
# 1-None and 4-5 returns 5-6
# 1-2, and 5-7 returns 6, 9
if min_a == 0:
( min_answer, max_answer ) = ( min_b, max_b )
elif min_b == 0:
( min_answer, max_answer ) = ( min_a, max_a )
else:
if max_a is None:
max_a = min_a
if max_b is None:
max_b = min_b
min_answer = max( min_a, min_b )
max_answer = max_a + max_b
return ( min_answer, max_answer )
def MergePredicates( predicates ):
master_predicate_dict = {}
for predicate in predicates:
# this works because predicate.__hash__ exists
if predicate in master_predicate_dict:
master_predicate_dict[ predicate ].AddCounts( predicate )
else:
master_predicate_dict[ predicate ] = predicate
return master_predicate_dict.values()
def ShowExceptionClient( e ):
( etype, value, tb ) = sys.exc_info()
if etype is None:
etype = type( e )
value = HydrusData.ToUnicode( e )
trace = ''.join( traceback.format_stack() )
else:
trace = ''.join( traceback.format_exception( etype, value, tb ) )
pretty_value = HydrusData.ToUnicode( value )
if os.linesep in pretty_value:
( first_line, anything_else ) = HydrusData.ToUnicode( value ).split( os.linesep, 1 )
trace = trace + os.linesep + anything_else
else:
first_line = pretty_value
job_key = ClientThreading.JobKey()
if isinstance( e, HydrusExceptions.ShutdownException ):
return
else:
if etype == wx.PyDeadObjectError:
HydrusData.Print( 'Got a PyDeadObjectError, which can probably be ignored, but here it is anyway:' )
HydrusData.Print( trace )
return
if hasattr( etype, '__name__' ): title = HydrusData.ToUnicode( etype.__name__ )
else: title = HydrusData.ToUnicode( etype )
job_key.SetVariable( 'popup_title', title )
job_key.SetVariable( 'popup_text_1', first_line )
job_key.SetVariable( 'popup_traceback', trace )
text = job_key.ToString()
HydrusData.Print( 'Exception:' )
HydrusData.DebugPrint( text )
HydrusGlobals.client_controller.pub( 'message', job_key )
time.sleep( 1 )
def ShowTextClient( text ):
job_key = ClientThreading.JobKey()
job_key.SetVariable( 'popup_text_1', HydrusData.ToUnicode( text ) )
text = job_key.ToString()
HydrusData.Print( text )
HydrusGlobals.client_controller.pub( 'message', job_key )
def SortTagsList( tags, sort_type ):
if sort_type in ( CC.SORT_BY_LEXICOGRAPHIC_DESC, CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC ):
reverse = True
else:
reverse = False
if sort_type in ( CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_ASC, CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC ):
def key( tag ):
if ':' in tag:
( namespace, subtag ) = tag.split( ':', 1 )
if namespace == '':
return ( '{', subtag )
else:
return ( namespace, subtag )
else:
return ( '{', tag ) # '{' is above 'z' in ascii, so this works for most situations
else:
key = None
tags.sort( key = key, reverse = reverse )
def WaitPolitely( page_key = None ):
if page_key is not None:
HydrusGlobals.client_controller.pub( 'waiting_politely', page_key, True )
time.sleep( HC.options[ 'website_download_polite_wait' ] )
if page_key is not None:
HydrusGlobals.client_controller.pub( 'waiting_politely', page_key, False )
class Booru( HydrusData.HydrusYAMLBase ):
yaml_tag = u'!Booru'
def __init__( self, name, search_url, search_separator, advance_by_page_num, thumb_classname, image_id, image_data, tag_classnames_to_namespaces ):
self._name = name
self._search_url = search_url
self._search_separator = search_separator
self._advance_by_page_num = advance_by_page_num
self._thumb_classname = thumb_classname
self._image_id = image_id
self._image_data = image_data
self._tag_classnames_to_namespaces = tag_classnames_to_namespaces
def GetData( self ): return ( self._search_url, self._search_separator, self._advance_by_page_num, self._thumb_classname, self._image_id, self._image_data, self._tag_classnames_to_namespaces )
def GetGalleryParsingInfo( self ): return ( self._search_url, self._advance_by_page_num, self._search_separator, self._thumb_classname )
def GetName( self ): return self._name
def GetNamespaces( self ): return self._tag_classnames_to_namespaces.values()
sqlite3.register_adapter( Booru, yaml.safe_dump )
class ClientOptions( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_CLIENT_OPTIONS
SERIALISABLE_VERSION = 2
def __init__( self, db_dir = None ):
HydrusSerialisable.SerialisableBase.__init__( self )
if db_dir is None:
db_dir = os.path.join( HC.BASE_DIR, 'db' )
self._dictionary = HydrusSerialisable.SerialisableDictionary()
self._lock = threading.Lock()
self._InitialiseDefaults( db_dir )
def _GetSerialisableInfo( self ):
with self._lock:
serialisable_info = self._dictionary.GetSerialisableTuple()
return serialisable_info
def _InitialiseDefaults( self, db_dir ):
self._dictionary[ 'booleans' ] = {}
self._dictionary[ 'booleans' ][ 'apply_all_parents_to_all_services' ] = False
self._dictionary[ 'booleans' ][ 'apply_all_siblings_to_all_services' ] = False
self._dictionary[ 'booleans' ][ 'filter_inbox_and_archive_predicates' ] = False
self._dictionary[ 'booleans' ][ 'waiting_politely_text' ] = False
self._dictionary[ 'booleans' ][ 'show_thumbnail_title_banner' ] = True
self._dictionary[ 'booleans' ][ 'show_thumbnail_page' ] = True
self._dictionary[ 'booleans' ][ 'disable_cv_for_gifs' ] = False
self._dictionary[ 'booleans' ][ 'add_parents_on_manage_tags' ] = True
self._dictionary[ 'booleans' ][ 'replace_siblings_on_manage_tags' ] = True
self._dictionary[ 'booleans' ][ 'show_related_tags' ] = False
self._dictionary[ 'booleans' ][ 'hide_message_manager_on_gui_iconise' ] = HC.PLATFORM_OSX
self._dictionary[ 'booleans' ][ 'hide_message_manager_on_gui_deactive' ] = False
self._dictionary[ 'booleans' ][ 'load_images_with_pil' ] = True
#
self._dictionary[ 'integers' ] = {}
self._dictionary[ 'integers' ][ 'video_buffer_size_mb' ] = 96
self._dictionary[ 'integers' ][ 'related_tags_width' ] = 150
self._dictionary[ 'integers' ][ 'related_tags_search_1_duration_ms' ] = 250
self._dictionary[ 'integers' ][ 'related_tags_search_2_duration_ms' ] = 2000
self._dictionary[ 'integers' ][ 'related_tags_search_3_duration_ms' ] = 6000
#
self._dictionary[ 'keys' ] = {}
self._dictionary[ 'keys' ][ 'default_tag_service_search_page' ] = CC.COMBINED_TAG_SERVICE_KEY.encode( 'hex' )
#
self._dictionary[ 'noneable_integers' ] = {}
self._dictionary[ 'noneable_integers' ][ 'forced_search_limit' ] = None
self._dictionary[ 'noneable_integers' ][ 'disk_cache_maintenance_mb' ] = 256
self._dictionary[ 'noneable_integers' ][ 'disk_cache_init_period' ] = 4
self._dictionary[ 'noneable_integers' ][ 'suggested_tags_width' ] = None
self._dictionary[ 'noneable_integers' ][ 'num_recent_tags' ] = None
self._dictionary[ 'noneable_integers' ][ 'maintenance_vacuum_period_days' ] = 30
#
client_files_default = os.path.join( db_dir, 'client_files' )
self._dictionary[ 'client_files_locations_ideal_weights' ] = [ ( HydrusPaths.ConvertAbsPathToPortablePath( client_files_default ), 1.0 ) ]
self._dictionary[ 'client_files_locations_resized_thumbnail_override' ] = None
self._dictionary[ 'client_files_locations_full_size_thumbnail_override' ] = None
#
self._dictionary[ 'default_import_tag_options' ] = HydrusSerialisable.SerialisableDictionary()
#
self._dictionary[ 'frame_locations' ] = {}
# remember size, remember position, last_size, last_pos, default gravity, default position, maximised, fullscreen
self._dictionary[ 'frame_locations' ][ 'file_import_status' ] = ( True, True, None, None, ( -1, -1 ), 'topleft', False, False )
self._dictionary[ 'frame_locations' ][ 'main_gui' ] = ( True, True, ( 640, 480 ), ( 20, 20 ), ( -1, -1 ), 'topleft', True, False )
self._dictionary[ 'frame_locations' ][ 'manage_options_dialog' ] = ( False, False, None, None, ( -1, -1 ), 'topleft', False, False )
self._dictionary[ 'frame_locations' ][ 'manage_tags_dialog' ] = ( False, False, None, None, ( -1, 1 ), 'topleft', False, False )
self._dictionary[ 'frame_locations' ][ 'manage_tags_frame' ] = ( False, False, None, None, ( -1, 1 ), 'topleft', False, False )
self._dictionary[ 'frame_locations' ][ 'media_viewer' ] = ( True, True, ( 640, 480 ), ( 70, 70 ), ( -1, -1 ), 'topleft', True, True )
self._dictionary[ 'frame_locations' ][ 'regular_dialog' ] = ( False, False, None, None, ( -1, -1 ), 'topleft', False, False )
self._dictionary[ 'frame_locations' ][ 'review_services' ] = ( False, True, None, None, ( -1, -1 ), 'topleft', False, False )
#
self._dictionary[ 'media_view' ] = HydrusSerialisable.SerialisableDictionary() # integer keys, so got to be cleverer dict
# media_show_action, preview_show_action, ( media_scale_up, media_scale_down, preview_scale_up, preview_scale_down, exact_zooms_only, scale_up_quality, scale_down_quality ) )
jpg_zoom_info = ( CC.MEDIA_VIEWER_SCALE_TO_CANVAS, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, False, CC.ZOOM_LANCZOS4, CC.ZOOM_AREA )
png_zoom_info = ( CC.MEDIA_VIEWER_SCALE_MAX_REGULAR, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, True, CC.ZOOM_NEAREST, CC.ZOOM_AREA )
gif_zoom_info = ( CC.MEDIA_VIEWER_SCALE_MAX_REGULAR, CC.MEDIA_VIEWER_SCALE_MAX_REGULAR, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, True, CC.ZOOM_LANCZOS4, CC.ZOOM_AREA )
flash_zoom_info = ( CC.MEDIA_VIEWER_SCALE_TO_CANVAS, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, False, CC.ZOOM_LINEAR, CC.ZOOM_LINEAR )
video_zoom_info = ( CC.MEDIA_VIEWER_SCALE_100, CC.MEDIA_VIEWER_SCALE_MAX_REGULAR, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, CC.MEDIA_VIEWER_SCALE_TO_CANVAS, True, CC.ZOOM_LANCZOS4, CC.ZOOM_AREA )
null_zoom_info = ( CC.MEDIA_VIEWER_SCALE_100, CC.MEDIA_VIEWER_SCALE_100, CC.MEDIA_VIEWER_SCALE_100, CC.MEDIA_VIEWER_SCALE_100, False, CC.ZOOM_LINEAR, CC.ZOOM_LINEAR )
self._dictionary[ 'media_view' ][ HC.IMAGE_JPEG ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, jpg_zoom_info )
self._dictionary[ 'media_view' ][ HC.IMAGE_PNG ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, png_zoom_info )
self._dictionary[ 'media_view' ][ HC.IMAGE_GIF ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, gif_zoom_info )
if HC.PLATFORM_WINDOWS:
self._dictionary[ 'media_view' ][ HC.APPLICATION_FLASH ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED, CC.MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED, flash_zoom_info )
else:
self._dictionary[ 'media_view' ][ HC.APPLICATION_FLASH ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, null_zoom_info )
self._dictionary[ 'media_view' ][ HC.APPLICATION_PDF ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, null_zoom_info )
self._dictionary[ 'media_view' ][ HC.VIDEO_AVI ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
self._dictionary[ 'media_view' ][ HC.VIDEO_FLV ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
self._dictionary[ 'media_view' ][ HC.VIDEO_MOV ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
self._dictionary[ 'media_view' ][ HC.VIDEO_MP4 ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
self._dictionary[ 'media_view' ][ HC.VIDEO_MPEG ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
self._dictionary[ 'media_view' ][ HC.VIDEO_MKV ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
self._dictionary[ 'media_view' ][ HC.VIDEO_WEBM ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
self._dictionary[ 'media_view' ][ HC.VIDEO_WMV ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
self._dictionary[ 'media_view' ][ HC.AUDIO_MP3 ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, null_zoom_info )
self._dictionary[ 'media_view' ][ HC.AUDIO_OGG ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, null_zoom_info )
self._dictionary[ 'media_view' ][ HC.AUDIO_FLAC ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, null_zoom_info )
self._dictionary[ 'media_view' ][ HC.AUDIO_WMA ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, null_zoom_info )
self._dictionary[ 'media_zooms' ] = [ 0.01, 0.05, 0.1, 0.15, 0.2, 0.3, 0.5, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.5, 2.0, 3.0, 5.0, 10.0, 20.0 ]
#
self._dictionary[ 'suggested_tags' ] = HydrusSerialisable.SerialisableDictionary()
self._dictionary[ 'suggested_tags' ][ 'favourites' ] = {}
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
loaded_dictionary = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_info )
for ( key, value ) in loaded_dictionary.items():
if isinstance( value, dict ):
self._dictionary[ key ].update( value )
else:
self._dictionary[ key ] = value
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
if version == 1:
loaded_dictionary = HydrusSerialisable.CreateFromSerialisableTuple( old_serialisable_info )
if 'media_view' in loaded_dictionary:
mimes = loaded_dictionary[ 'media_view' ].keys()
for mime in mimes:
if mime in self._dictionary[ 'media_view' ]:
( default_media_show_action, default_preview_show_action, default_zoom_info ) = self._dictionary[ 'media_view' ][ mime ]
( media_show_action, preview_show_action, zoom_in_to_fit, exact_zooms_only, scale_up_quality, scale_down_quality ) = loaded_dictionary[ 'media_view' ][ mime ]
loaded_dictionary[ 'media_view' ][ mime ] = ( media_show_action, preview_show_action, default_zoom_info )
else:
# while devving this, I discovered some u'20' stringified keys had snuck in and hung around. let's nuke them here, in case anyone else got similar
del loaded_dictionary[ 'media_view' ][ mime ]
new_serialisable_info = loaded_dictionary.GetSerialisableTuple()
return ( 2, new_serialisable_info )
def ClearDefaultImportTagOptions( self ):
with self._lock:
self._dictionary[ 'default_import_tag_options' ] = HydrusSerialisable.SerialisableDictionary()
def GetBoolean( self, name ):
with self._lock:
return self._dictionary[ 'booleans' ][ name ]
def GetClientFilesLocationsToIdealWeights( self ):
with self._lock:
paths_to_weights = {}
for ( portable_path, weight ) in self._dictionary[ 'client_files_locations_ideal_weights' ]:
abs_path = HydrusPaths.ConvertPortablePathToAbsPath( portable_path )
paths_to_weights[ abs_path ] = weight
resized_thumbnail_override = self._dictionary[ 'client_files_locations_resized_thumbnail_override' ]
if resized_thumbnail_override is not None:
resized_thumbnail_override = HydrusPaths.ConvertPortablePathToAbsPath( resized_thumbnail_override )
full_size_thumbnail_override = self._dictionary[ 'client_files_locations_full_size_thumbnail_override' ]
if full_size_thumbnail_override is not None:
full_size_thumbnail_override = HydrusPaths.ConvertPortablePathToAbsPath( full_size_thumbnail_override )
return ( paths_to_weights, resized_thumbnail_override, full_size_thumbnail_override )
def GetDefaultImportTagOptions( self, gallery_identifier = None ):
with self._lock:
default_import_tag_options = self._dictionary[ 'default_import_tag_options' ]
if gallery_identifier is None:
return default_import_tag_options
else:
if gallery_identifier in default_import_tag_options:
import_tag_options = default_import_tag_options[ gallery_identifier ]
else:
default_booru_gallery_identifier = ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_BOORU )
default_hentai_foundry_gallery_identifier = ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_HENTAI_FOUNDRY )
default_pixiv_gallery_identifier = ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_PIXIV )
default_gallery_identifier = ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_DEFAULT )
guidance_import_tag_options = None
site_type = gallery_identifier.GetSiteType()
if site_type == HC.SITE_TYPE_BOORU and default_booru_gallery_identifier in default_import_tag_options:
guidance_import_tag_options = default_import_tag_options[ default_booru_gallery_identifier ]
elif site_type in ( HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST, HC.SITE_TYPE_HENTAI_FOUNDRY_TAGS ) and default_hentai_foundry_gallery_identifier in default_import_tag_options:
guidance_import_tag_options = default_import_tag_options[ default_hentai_foundry_gallery_identifier ]
elif site_type in ( HC.SITE_TYPE_PIXIV_ARTIST_ID, HC.SITE_TYPE_PIXIV_TAG ) and default_pixiv_gallery_identifier in default_import_tag_options:
guidance_import_tag_options = default_import_tag_options[ default_pixiv_gallery_identifier ]
elif default_gallery_identifier in default_import_tag_options:
guidance_import_tag_options = default_import_tag_options[ default_gallery_identifier ]
service_keys_to_namespaces = {}
service_keys_to_explicit_tags = {}
if guidance_import_tag_options is not None:
( namespaces, search_value ) = ClientDefaults.GetDefaultNamespacesAndSearchValue( gallery_identifier )
guidance_service_keys_to_namespaces = guidance_import_tag_options.GetServiceKeysToNamespaces()
for ( service_key, guidance_namespaces ) in guidance_service_keys_to_namespaces.items():
if 'all namespaces' in guidance_namespaces:
service_keys_to_namespaces[ service_key ] = namespaces
else:
service_keys_to_namespaces[ service_key ] = [ namespace for namespace in namespaces if namespace in guidance_namespaces ]
service_keys_to_explicit_tags = guidance_import_tag_options.GetServiceKeysToExplicitTags()
import_tag_options = ImportTagOptions( service_keys_to_namespaces = service_keys_to_namespaces, service_keys_to_explicit_tags = service_keys_to_explicit_tags )
return import_tag_options
def GetFrameLocation( self, frame_key ):
with self._lock:
return self._dictionary[ 'frame_locations' ][ frame_key ]
def GetFrameLocations( self ):
with self._lock:
return self._dictionary[ 'frame_locations' ].items()
def GetInteger( self, name ):
with self._lock:
return self._dictionary[ 'integers' ][ name ]
def GetKey( self, name ):
with self._lock:
return self._dictionary[ 'keys' ][ name ].decode( 'hex' )
def GetMediaShowAction( self, mime ):
with self._lock:
( media_show_action, preview_show_action, zoom_info ) = self._dictionary[ 'media_view' ][ mime ]
return media_show_action
def GetMediaViewOptions( self, mime ):
with self._lock:
return self._dictionary[ 'media_view' ][ mime ]
def GetMediaZoomOptions( self, mime ):
with self._lock:
( media_show_action, preview_show_action, zoom_info ) = self._dictionary[ 'media_view' ][ mime ]
return zoom_info
def GetMediaZooms( self ):
with self._lock:
return list( self._dictionary[ 'media_zooms' ] )
def GetMediaZoomQuality( self, mime ):
with self._lock:
( media_show_action, preview_show_action, ( media_scale_up, media_scale_down, preview_scale_up, preview_scale_down, exact_zooms_only, scale_up_quality, scale_down_quality ) ) = self._dictionary[ 'media_view' ][ mime ]
return ( scale_up_quality, scale_down_quality )
def GetNoneableInteger( self, name ):
with self._lock:
return self._dictionary[ 'noneable_integers' ][ name ]
def GetPreviewShowAction( self, mime ):
with self._lock:
( media_show_action, preview_show_action, zoom_info ) = self._dictionary[ 'media_view' ][ mime ]
return preview_show_action
def GetSuggestedTagsFavourites( self, service_key ):
with self._lock:
service_key_hex = service_key.encode( 'hex' )
stf = self._dictionary[ 'suggested_tags' ][ 'favourites' ]
if service_key_hex in stf:
return set( stf[ service_key_hex ] )
else:
return set()
def SetBoolean( self, name, value ):
with self._lock:
self._dictionary[ 'booleans' ][ name ] = value
def SetClientFilesLocationsToIdealWeights( self, locations_to_weights, resized_thumbnail_override, full_size_thumbnail_override ):
with self._lock:
portable_locations_and_weights = [ ( HydrusPaths.ConvertAbsPathToPortablePath( location ), float( weight ) ) for ( location, weight ) in locations_to_weights.items() ]
self._dictionary[ 'client_files_locations_ideal_weights' ] = portable_locations_and_weights
if resized_thumbnail_override is not None:
resized_thumbnail_override = HydrusPaths.ConvertAbsPathToPortablePath( resized_thumbnail_override )
self._dictionary[ 'client_files_locations_resized_thumbnail_override' ] = resized_thumbnail_override
self._dictionary[ 'client_files_locations_full_size_thumbnail_override' ] = full_size_thumbnail_override
def SetDefaultImportTagOptions( self, gallery_identifier, import_tag_options ):
with self._lock:
self._dictionary[ 'default_import_tag_options' ][ gallery_identifier ] = import_tag_options
def SetFrameLocation( self, frame_key, remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ):
with self._lock:
self._dictionary[ 'frame_locations' ][ frame_key ] = ( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen )
def SetInteger( self, name, value ):
with self._lock:
self._dictionary[ 'integers' ][ name ] = value
def SetKey( self, name, value ):
with self._lock:
self._dictionary[ 'keys' ][ name ] = value.encode( 'hex' )
def SetMediaViewOptions( self, mime, value_tuple ):
with self._lock:
self._dictionary[ 'media_view' ][ mime ] = value_tuple
def SetMediaZooms( self, zooms ):
with self._lock:
self._dictionary[ 'media_zooms' ] = zooms
def SetNoneableInteger( self, name, value ):
with self._lock:
self._dictionary[ 'noneable_integers' ][ name ] = value
def SetSuggestedTagsFavourites( self, service_key, tags ):
with self._lock:
service_key_hex = service_key.encode( 'hex' )
self._dictionary[ 'suggested_tags' ][ 'favourites' ][ service_key_hex ] = list( tags )
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_CLIENT_OPTIONS ] = ClientOptions
class CommandHandler( object ):
def __init__( self, action_cache ):
self._action_cache = action_cache
self._commands_to_callables = {}
def ProcessMenuEvent( self, menu_event ):
event_id = menu_event.GetId()
action = self._action_cache.GetAction( event_id )
if action is not None:
( command, data ) = action
if command in self._commands_to_callables:
callable = self._commands_to_callables[ command ]
if data is None:
wx.CallAfter( callable )
else:
wx.CallAfter( callable, data )
def SetCallable( self, command, callable ):
self._commands_to_callables[ command ] = callable
class Credentials( HydrusData.HydrusYAMLBase ):
yaml_tag = u'!Credentials'
def __init__( self, host, port, access_key = None ):
HydrusData.HydrusYAMLBase.__init__( self )
if host == 'localhost': host = '127.0.0.1'
self._host = host
self._port = port
self._access_key = access_key
def __eq__( self, other ): return self.__hash__() == other.__hash__()
def __hash__( self ): return ( self._host, self._port, self._access_key ).__hash__()
def __ne__( self, other ): return self.__hash__() != other.__hash__()
def __repr__( self ): return 'Credentials: ' + HydrusData.ToUnicode( ( self._host, self._port, self._access_key.encode( 'hex' ) ) )
def GetAccessKey( self ): return self._access_key
def GetAddress( self ): return ( self._host, self._port )
def GetConnectionString( self ):
connection_string = ''
if self.HasAccessKey(): connection_string += self._access_key.encode( 'hex' ) + '@'
connection_string += self._host + ':' + str( self._port )
return connection_string
def HasAccessKey( self ): return self._access_key is not None and self._access_key is not ''
def SetAccessKey( self, access_key ): self._access_key = access_key
class Imageboard( HydrusData.HydrusYAMLBase ):
yaml_tag = u'!Imageboard'
def __init__( self, name, post_url, flood_time, form_fields, restrictions ):
self._name = name
self._post_url = post_url
self._flood_time = flood_time
self._form_fields = form_fields
self._restrictions = restrictions
def IsOkToPost( self, media_result ):
( hash, inbox, size, mime, width, height, duration, num_frames, num_words, tags_manager, locations_manager, ratings_manager ) = media_result.ToTuple()
if CC.RESTRICTION_MIN_RESOLUTION in self._restrictions:
( min_width, min_height ) = self._restrictions[ CC.RESTRICTION_MIN_RESOLUTION ]
if width < min_width or height < min_height: return False
if CC.RESTRICTION_MAX_RESOLUTION in self._restrictions:
( max_width, max_height ) = self._restrictions[ CC.RESTRICTION_MAX_RESOLUTION ]
if width > max_width or height > max_height: return False
if CC.RESTRICTION_MAX_FILE_SIZE in self._restrictions and size > self._restrictions[ CC.RESTRICTION_MAX_FILE_SIZE ]: return False
if CC.RESTRICTION_ALLOWED_MIMES in self._restrictions and mime not in self._restrictions[ CC.RESTRICTION_ALLOWED_MIMES ]: return False
return True
def GetBoardInfo( self ): return ( self._post_url, self._flood_time, self._form_fields, self._restrictions )
def GetName( self ): return self._name
sqlite3.register_adapter( Imageboard, yaml.safe_dump )
class ImportFileOptions( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FILE_OPTIONS
SERIALISABLE_VERSION = 1
def __init__( self, automatic_archive = None, exclude_deleted = None, min_size = None, min_resolution = None ):
HydrusSerialisable.SerialisableBase.__init__( self )
self._automatic_archive = automatic_archive
self._exclude_deleted = exclude_deleted
self._min_size = min_size
self._min_resolution = min_resolution
def _GetSerialisableInfo( self ):
return ( self._automatic_archive, self._exclude_deleted, self._min_size, self._min_resolution )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( self._automatic_archive, self._exclude_deleted, self._min_size, self._min_resolution ) = serialisable_info
def FileIsValid( self, size, resolution = None ):
if self._min_size is not None and size < self._min_size:
return False
if resolution is not None and self._min_resolution is not None:
( x, y ) = resolution
( min_x, min_y ) = self._min_resolution
if x < min_x or y < min_y:
return False
return True
def GetAutomaticArchive( self ):
return self._automatic_archive
def GetExcludeDeleted( self ):
return self._exclude_deleted
def ToTuple( self ):
return ( self._automatic_archive, self._exclude_deleted, self._min_size, self._min_resolution )
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FILE_OPTIONS ] = ImportFileOptions
class ImportTagOptions( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_TAG_OPTIONS
SERIALISABLE_VERSION = 2
def __init__( self, service_keys_to_namespaces = None, service_keys_to_explicit_tags = None ):
HydrusSerialisable.SerialisableBase.__init__( self )
if service_keys_to_namespaces is None:
service_keys_to_namespaces = {}
if service_keys_to_explicit_tags is None:
service_keys_to_explicit_tags = {}
self._service_keys_to_namespaces = service_keys_to_namespaces
self._service_keys_to_explicit_tags = service_keys_to_explicit_tags
def _GetSerialisableInfo( self ):
safe_service_keys_to_namespaces = { service_key.encode( 'hex' ) : list( namespaces ) for ( service_key, namespaces ) in self._service_keys_to_namespaces.items() }
safe_service_keys_to_explicit_tags = { service_key.encode( 'hex' ) : list( tags ) for ( service_key, tags ) in self._service_keys_to_explicit_tags.items() }
return ( safe_service_keys_to_namespaces, safe_service_keys_to_explicit_tags )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( safe_service_keys_to_namespaces, safe_service_keys_to_explicit_tags ) = serialisable_info
self._service_keys_to_namespaces = { service_key.decode( 'hex' ) : set( namespaces ) for ( service_key, namespaces ) in safe_service_keys_to_namespaces.items() }
self._service_keys_to_explicit_tags = { service_key.decode( 'hex' ) : set( tags ) for ( service_key, tags ) in safe_service_keys_to_explicit_tags.items() }
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
if version == 1:
safe_service_keys_to_namespaces = old_serialisable_info
safe_service_keys_to_explicit_tags = {}
new_serialisable_info = ( safe_service_keys_to_namespaces, safe_service_keys_to_explicit_tags )
return ( 2, new_serialisable_info )
def GetServiceKeysToExplicitTags( self ):
return dict( self._service_keys_to_explicit_tags )
def GetServiceKeysToNamespaces( self ):
return dict( self._service_keys_to_namespaces )
def GetServiceKeysToContentUpdates( self, hash, tags ):
tags = [ tag for tag in tags if tag is not None ]
service_keys_to_tags = collections.defaultdict( set )
siblings_manager = HydrusGlobals.client_controller.GetManager( 'tag_siblings' )
parents_manager = HydrusGlobals.client_controller.GetManager( 'tag_parents' )
for ( service_key, namespaces ) in self._service_keys_to_namespaces.items():
tags_to_add_here = []
if len( namespaces ) > 0:
for namespace in namespaces:
if namespace == '': tags_to_add_here.extend( [ tag for tag in tags if not ':' in tag ] )
else: tags_to_add_here.extend( [ tag for tag in tags if tag.startswith( namespace + ':' ) ] )
tags_to_add_here = HydrusTags.CleanTags( tags_to_add_here )
if len( tags_to_add_here ) > 0:
tags_to_add_here = siblings_manager.CollapseTags( service_key, tags_to_add_here )
tags_to_add_here = parents_manager.ExpandTags( service_key, tags_to_add_here )
service_keys_to_tags[ service_key ].update( tags_to_add_here )
for ( service_key, explicit_tags ) in self._service_keys_to_explicit_tags.items():
tags_to_add_here = HydrusTags.CleanTags( explicit_tags )
if len( tags_to_add_here ) > 0:
tags_to_add_here = siblings_manager.CollapseTags( service_key, tags_to_add_here )
tags_to_add_here = parents_manager.ExpandTags( service_key, tags_to_add_here )
service_keys_to_tags[ service_key ].update( tags_to_add_here )
service_keys_to_content_updates = ConvertServiceKeysToTagsToServiceKeysToContentUpdates( { hash }, service_keys_to_tags )
return service_keys_to_content_updates
def ShouldFetchTags( self ):
i_am_interested_in_namespaces = len( self._service_keys_to_namespaces ) > 0
return i_am_interested_in_namespaces
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_TAG_OPTIONS ] = ImportTagOptions
class Service( object ):
def __init__( self, service_key, service_type, name, info ):
self._service_key = service_key
self._service_type = service_type
self._name = name
self._info = info
self._lock = threading.Lock()
HydrusGlobals.client_controller.sub( self, 'ProcessServiceUpdates', 'service_updates_data' )
def __hash__( self ): return self._service_key.__hash__()
def _ProcessServiceUpdate( self, service_update ):
pass
def GetInfo( self, key = None ):
if key is None:
return self._info
else:
return self._info[ key ]
def GetServiceKey( self ): return self._service_key
def GetName( self ): return self._name
def GetServiceType( self ): return self._service_type
def ProcessServiceUpdates( self, service_keys_to_service_updates ):
for ( service_key, service_updates ) in service_keys_to_service_updates.items():
for service_update in service_updates:
if service_key == self._service_key:
self._ProcessServiceUpdate( service_update )
def ToTuple( self ): return ( self._service_key, self._service_type, self._name, self._info )
class ServiceLocalBooru( Service ):
def _ProcessServiceUpdate( self, service_update ):
Service._ProcessServiceUpdate( self, service_update )
( action, row ) = service_update.ToTuple()
if action == HC.SERVICE_UPDATE_REQUEST_MADE:
num_bytes = row
self._info[ 'used_monthly_data' ] += num_bytes
self._info[ 'used_monthly_requests' ] += 1
class ServiceRemote( Service ):
def GetCredentials( self ):
host = self._info[ 'host' ]
port = self._info[ 'port' ]
credentials = Credentials( host, port )
return credentials
def _GetErrorWaitPeriod( self ):
return 3600 * 4
def _ProcessServiceUpdate( self, service_update ):
Service._ProcessServiceUpdate( self, service_update )
( action, row ) = service_update.ToTuple()
if action == HC.SERVICE_UPDATE_ERROR:
self._info[ 'last_error' ] = HydrusData.GetNow()
elif action == HC.SERVICE_UPDATE_RESET:
self._info[ 'last_error' ] = 0
def GetRecentErrorPending( self ):
return HydrusData.ConvertTimestampToPrettyPending( self._info[ 'last_error' ] + self._GetErrorWaitPeriod() )
def HasRecentError( self ):
return not HydrusData.TimeHasPassed( self._info[ 'last_error' ] + self._GetErrorWaitPeriod() )
def SetCredentials( self, credentials ):
( host, port ) = credentials.GetAddress()
self._info[ 'host' ] = host
self._info[ 'port' ] = port
class ServiceRestricted( ServiceRemote ):
def _GetErrorWaitPeriod( self ):
if 'account' in self._info and self._info[ 'account' ].HasPermission( HC.GENERAL_ADMIN ):
return 900
else:
return ServiceRemote._GetErrorWaitPeriod( self )
def _ProcessServiceUpdate( self, service_update ):
ServiceRemote._ProcessServiceUpdate( self, service_update )
( action, row ) = service_update.ToTuple()
if action == HC.SERVICE_UPDATE_ACCOUNT:
account = row
self._info[ 'account' ] = account
self._info[ 'last_error' ] = 0
elif action == HC.SERVICE_UPDATE_REQUEST_MADE:
num_bytes = row
self._info[ 'account' ].RequestMade( num_bytes )
def _RecordHydrusBandwidth( self, method, command, data_used ):
if ( self._service_type, method, command ) in HC.BANDWIDTH_CONSUMING_REQUESTS:
HydrusGlobals.client_controller.pub( 'service_updates_delayed', { self._service_key : [ HydrusData.ServiceUpdate( HC.SERVICE_UPDATE_REQUEST_MADE, data_used ) ] } )
def CanDownload( self ):
return self._info[ 'account' ].HasPermission( HC.GET_DATA ) and not self.HasRecentError()
def CanUpload( self ): return self._info[ 'account' ].HasPermission( HC.POST_DATA ) and not self.HasRecentError()
def GetCredentials( self ):
host = self._info[ 'host' ]
port = self._info[ 'port' ]
if 'access_key' in self._info:
access_key = self._info[ 'access_key' ]
else:
access_key = None
credentials = Credentials( host, port, access_key )
return credentials
def IsInitialised( self ):
if self._service_type == HC.SERVER_ADMIN:
return 'access_key' in self._info
else:
return True
def Request( self, method, command, request_args = None, request_headers = None, report_hooks = None, temp_path = None, return_cookies = False ):
if request_args is None: request_args = {}
if request_headers is None: request_headers = {}
if report_hooks is None: report_hooks = []
try:
credentials = self.GetCredentials()
if command in ( 'access_key', 'init', '' ):
pass
elif command in ( 'session_key', 'access_key_verification' ):
ClientNetworking.AddHydrusCredentialsToHeaders( credentials, request_headers )
else:
ClientNetworking.AddHydrusSessionKeyToHeaders( self._service_key, request_headers )
path = '/' + command
if method == HC.GET:
query = ClientNetworking.ConvertHydrusGETArgsToQuery( request_args )
body = ''
elif method == HC.POST:
query = ''
if command == 'file':
content_type = HC.APPLICATION_OCTET_STREAM
body = request_args[ 'file' ]
del request_args[ 'file' ]
else:
if isinstance( request_args, HydrusSerialisable.SerialisableDictionary ):
content_type = HC.APPLICATION_JSON
body = request_args.DumpToNetworkString()
else:
content_type = HC.APPLICATION_YAML
body = yaml.safe_dump( request_args )
request_headers[ 'Content-Type' ] = HC.mime_string_lookup[ content_type ]
if query != '':
path_and_query = path + '?' + query
else:
path_and_query = path
( host, port ) = credentials.GetAddress()
url = 'http://' + host + ':' + str( port ) + path_and_query
( response, size_of_response, response_headers, cookies ) = HydrusGlobals.client_controller.DoHTTP( method, url, request_headers, body, report_hooks = report_hooks, temp_path = temp_path, return_everything = True )
ClientNetworking.CheckHydrusVersion( self._service_key, self._service_type, response_headers )
if method == HC.GET:
data_used = size_of_response
elif method == HC.POST:
data_used = len( body )
self._RecordHydrusBandwidth( method, command, data_used )
if return_cookies:
return ( response, cookies )
else:
return response
except Exception as e:
if not isinstance( e, HydrusExceptions.ServerBusyException ):
if isinstance( e, HydrusExceptions.SessionException ):
session_manager = HydrusGlobals.client_controller.GetClientSessionManager()
session_manager.DeleteSessionKey( self._service_key )
HydrusGlobals.client_controller.Write( 'service_updates', { self._service_key : [ HydrusData.ServiceUpdate( HC.SERVICE_UPDATE_ERROR, HydrusData.ToUnicode( e ) ) ] } )
if isinstance( e, HydrusExceptions.PermissionException ):
if 'account' in self._info:
account_key = self._info[ 'account' ].GetAccountKey()
unknown_account = HydrusData.GetUnknownAccount( account_key )
else: unknown_account = HydrusData.GetUnknownAccount()
HydrusGlobals.client_controller.Write( 'service_updates', { self._service_key : [ HydrusData.ServiceUpdate( HC.SERVICE_UPDATE_ACCOUNT, unknown_account ) ] } )
raise
def SetCredentials( self, credentials ):
ServiceRemote.SetCredentials( self, credentials )
if credentials.HasAccessKey():
self._info[ 'access_key' ] = credentials.GetAccessKey()
class ServiceRepository( ServiceRestricted ):
def _ProcessServiceUpdate( self, service_update ):
ServiceRestricted._ProcessServiceUpdate( self, service_update )
( action, row ) = service_update.ToTuple()
if action == HC.SERVICE_UPDATE_RESET:
if 'next_processing_timestamp' in self._info:
self._info[ 'next_processing_timestamp' ] = 0
elif action == HC.SERVICE_UPDATE_NEXT_DOWNLOAD_TIMESTAMP:
next_download_timestamp = row
if next_download_timestamp > self._info[ 'next_download_timestamp' ]:
if self._info[ 'first_timestamp' ] is None: self._info[ 'first_timestamp' ] = next_download_timestamp
self._info[ 'next_download_timestamp' ] = next_download_timestamp
elif action == HC.SERVICE_UPDATE_NEXT_PROCESSING_TIMESTAMP:
next_processing_timestamp = row
if next_processing_timestamp > self._info[ 'next_processing_timestamp' ]:
self._info[ 'next_processing_timestamp' ] = next_processing_timestamp
elif action == HC.SERVICE_UPDATE_PAUSE:
self._info[ 'paused' ] = True
def _ReportSyncProcessingError( self, path, error_text ):
text = 'While synchronising ' + self._name + ', the expected update file ' + path + ', ' + error_text + '.'
text += os.linesep * 2
text += 'The service has been indefinitely paused.'
text += os.linesep * 2
text += 'This is a serious error. Unless you know what went wrong, you should contact the developer.'
HydrusData.ShowText( text )
service_updates = [ HydrusData.ServiceUpdate( HC.SERVICE_UPDATE_PAUSE ) ]
service_keys_to_service_updates = { self._service_key : service_updates }
self.ProcessServiceUpdates( service_keys_to_service_updates )
HydrusGlobals.client_controller.Write( 'service_updates', service_keys_to_service_updates )
def CanDownloadUpdate( self ):
work_to_do = self.IsUpdateDueForDownload()
return work_to_do and self.CanDownload() and not self.IsPaused()
def CanProcessUpdate( self ):
work_to_do = self.IsUpdateDueForProcessing()
it_is_time = HydrusData.TimeHasPassed( self._info[ 'next_processing_timestamp' ] + HC.UPDATE_DURATION + HC.options[ 'processing_phase' ] )
return work_to_do and it_is_time and not self.IsPaused()
def GetTimestamps( self ): return ( self._info[ 'first_timestamp' ], self._info[ 'next_download_timestamp' ], self._info[ 'next_processing_timestamp' ] )
def GetUpdateStatus( self ):
account = self._info[ 'account' ]
now = HydrusData.GetNow()
first_timestamp = self._info[ 'first_timestamp' ]
next_download_timestamp = self._info[ 'next_download_timestamp' ]
next_processing_timestamp = self._info[ 'next_processing_timestamp' ]
if first_timestamp is None:
num_updates = 0
num_updates_downloaded = 0
num_updates_processed = 0
else:
num_updates = ( now - first_timestamp ) / HC.UPDATE_DURATION
num_updates_downloaded = ( next_download_timestamp - first_timestamp ) / HC.UPDATE_DURATION
num_updates_processed = max( 0, ( next_processing_timestamp - first_timestamp ) / HC.UPDATE_DURATION )
downloaded_text = 'downloaded ' + HydrusData.ConvertValueRangeToPrettyString( num_updates_downloaded, num_updates )
processed_text = 'processed ' + HydrusData.ConvertValueRangeToPrettyString( num_updates_processed, num_updates )
if self.IsPaused() or not self._info[ 'account' ].HasPermission( HC.GET_DATA ): status = 'updates on hold'
else:
if self.CanDownloadUpdate(): status = 'downloaded up to ' + HydrusData.ConvertTimestampToPrettySync( self._info[ 'next_download_timestamp' ] )
elif self.CanProcessUpdate(): status = 'processed up to ' + HydrusData.ConvertTimestampToPrettySync( self._info[ 'next_processing_timestamp' ] )
elif self.HasRecentError(): status = 'due to a previous error, update is delayed - next check ' + self.GetRecentErrorPending()
else:
if HydrusData.TimeHasPassed( self._info[ 'next_download_timestamp' ] + HC.UPDATE_DURATION ):
status = 'next update will be downloaded soon'
else:
status = 'fully synchronised - next update ' + HydrusData.ConvertTimestampToPrettyPending( self._info[ 'next_download_timestamp' ] + HC.UPDATE_DURATION + 1800 )
return downloaded_text + ' - ' + processed_text + ' - ' + status
def IsPaused( self ):
return self._info[ 'paused' ]
def IsUpdateDueForDownload( self ):
return HydrusData.TimeHasPassed( self._info[ 'next_download_timestamp' ] + HC.UPDATE_DURATION + 1800 )
def IsUpdateDueForProcessing( self ):
return self._info[ 'next_download_timestamp' ] > self._info[ 'next_processing_timestamp' ]
def Sync( self, only_when_idle = False, stop_time = None ):
if self.IsPaused():
return
if not self.CanDownloadUpdate() and not self.CanProcessUpdate():
return
job_key = ClientThreading.JobKey( pausable = False, cancellable = True, only_when_idle = only_when_idle, stop_time = stop_time )
( i_paused, should_quit ) = job_key.WaitIfNeeded()
if should_quit:
return
num_updates_downloaded = 0
num_updates_processed = 0
total_content_weight_processed = 0
try:
options = HydrusGlobals.client_controller.GetOptions()
HydrusGlobals.client_controller.pub( 'splash_set_title_text', self._name )
job_key.SetVariable( 'popup_title', 'repository synchronisation - ' + self._name )
HydrusGlobals.client_controller.pub( 'message', job_key )
try:
while self.CanDownloadUpdate():
if options[ 'pause_repo_sync' ]:
break
( i_paused, should_quit ) = job_key.WaitIfNeeded()
if should_quit:
break
if self._info[ 'first_timestamp' ] is None:
gauge_range = None
gauge_value = 0
update_index_string = 'initial update: '
else:
gauge_range = ( HydrusData.GetNow() - self._info[ 'first_timestamp' ] ) / HC.UPDATE_DURATION
gauge_value = ( ( self._info[ 'next_download_timestamp' ] - self._info[ 'first_timestamp' ] ) / HC.UPDATE_DURATION ) + 1
update_index_string = 'update ' + HydrusData.ConvertValueRangeToPrettyString( gauge_value, gauge_range ) + ': '
subupdate_index_string = 'service update: '
HydrusGlobals.client_controller.pub( 'splash_set_title_text', self._name + ' - ' + update_index_string + subupdate_index_string )
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'downloading' )
job_key.SetVariable( 'popup_text_1', update_index_string + subupdate_index_string + 'downloading and parsing' )
job_key.SetVariable( 'popup_gauge_1', ( gauge_value, gauge_range ) )
service_update_package = self.Request( HC.GET, 'service_update_package', { 'begin' : self._info[ 'next_download_timestamp' ] } )
begin = service_update_package.GetBegin()
subindex_count = service_update_package.GetSubindexCount()
for subindex in range( subindex_count ):
path = ClientFiles.GetExpectedContentUpdatePackagePath( self._service_key, begin, subindex )
if os.path.exists( path ):
size = os.path.getsize( path )
if size == 0:
os.remove( path )
if not os.path.exists( path ):
subupdate_index_string = 'content update ' + HydrusData.ConvertValueRangeToPrettyString( subindex + 1, subindex_count ) + ': '
HydrusGlobals.client_controller.pub( 'splash_set_title_text', self._name + ' - ' + update_index_string + subupdate_index_string )
job_key.SetVariable( 'popup_text_1', update_index_string + subupdate_index_string + 'downloading and parsing' )
content_update_package = self.Request( HC.GET, 'content_update_package', { 'begin' : begin, 'subindex' : subindex } )
obj_string = content_update_package.DumpToNetworkString()
job_key.SetVariable( 'popup_text_1', update_index_string + subupdate_index_string + 'saving to disk' )
with open( path, 'wb' ) as f: f.write( obj_string )
job_key.SetVariable( 'popup_text_1', update_index_string + 'committing' )
path = ClientFiles.GetExpectedServiceUpdatePackagePath( self._service_key, begin )
obj_string = service_update_package.DumpToNetworkString()
with open( path, 'wb' ) as f: f.write( obj_string )
service_updates = [ HydrusData.ServiceUpdate( HC.SERVICE_UPDATE_NEXT_DOWNLOAD_TIMESTAMP, service_update_package.GetNextBegin() ) ]
service_keys_to_service_updates = { self._service_key : service_updates }
self.ProcessServiceUpdates( service_keys_to_service_updates )
HydrusGlobals.client_controller.Write( 'service_updates', service_keys_to_service_updates )
HydrusGlobals.client_controller.WaitUntilPubSubsEmpty()
num_updates_downloaded += 1
except HydrusExceptions.ServerBusyException:
job_key.SetVariable( 'popup_text_1', 'Server was too busy to respond for now, will continue with processing.' )
time.sleep( 3 )
except Exception as e:
if 'Could not connect' in str( e ):
job_key.SetVariable( 'popup_text_1', 'Could not connect to service, will continue with processing.' )
time.sleep( 5 )
else:
raise
if self._service_type == HC.TAG_REPOSITORY and self.CanProcessUpdate():
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing disk cache' )
job_key.SetVariable( 'popup_text_1', 'preparing disk cache' )
loaded_into_disk_cache = False
while not loaded_into_disk_cache:
if options[ 'pause_repo_sync' ]:
break
( i_paused, should_quit ) = job_key.WaitIfNeeded()
if should_quit:
break
stop_time = HydrusData.GetNow() + 15
loaded_into_disk_cache = HydrusGlobals.client_controller.Read( 'load_into_disk_cache', stop_time = stop_time )
while self.CanProcessUpdate():
if options[ 'pause_repo_sync' ]:
break
( i_paused, should_quit ) = job_key.WaitIfNeeded()
if should_quit:
break
gauge_range = ( ( HydrusData.GetNow() - self._info[ 'first_timestamp' ] ) / HC.UPDATE_DURATION )
gauge_value = ( ( self._info[ 'next_processing_timestamp' ] - self._info[ 'first_timestamp' ] ) / HC.UPDATE_DURATION ) + 1
update_index_string = 'update ' + HydrusData.ConvertValueRangeToPrettyString( gauge_value, gauge_range ) + ': '
subupdate_index_string = 'service update: '
HydrusGlobals.client_controller.pub( 'splash_set_title_text', self._name + ' - ' + update_index_string + subupdate_index_string )
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'processing' )
job_key.SetVariable( 'popup_text_1', update_index_string + subupdate_index_string + 'loading from disk' )
job_key.SetVariable( 'popup_gauge_1', ( gauge_value, gauge_range ) )
path = ClientFiles.GetExpectedServiceUpdatePackagePath( self._service_key, self._info[ 'next_processing_timestamp' ] )
if not os.path.exists( path ):
self._ReportSyncProcessingError( path, 'was missing' )
return
with open( path, 'rb' ) as f: obj_string = f.read()
try:
service_update_package = HydrusSerialisable.CreateFromNetworkString( obj_string )
if not isinstance( service_update_package, HydrusData.ServerToClientServiceUpdatePackage ):
raise Exception()
except:
self._ReportSyncProcessingError( path, 'did not parse' )
return
subindex_count = service_update_package.GetSubindexCount()
processing_went_ok = True
for subindex in range( subindex_count ):
should_break = False
if options[ 'pause_repo_sync' ]:
should_break = True
( i_paused, should_quit ) = job_key.WaitIfNeeded()
if should_quit or should_break:
processing_went_ok = False
break
subupdate_index_string = 'content update ' + HydrusData.ConvertValueRangeToPrettyString( subindex + 1, subindex_count ) + ': '
path = ClientFiles.GetExpectedContentUpdatePackagePath( self._service_key, self._info[ 'next_processing_timestamp' ], subindex )
if not os.path.exists( path ):
self._ReportSyncProcessingError( path, 'was missing' )
return
job_key.SetVariable( 'popup_text_1', update_index_string + subupdate_index_string + 'loading from disk' )
with open( path, 'rb' ) as f: obj_string = f.read()
job_key.SetVariable( 'popup_text_1', update_index_string + subupdate_index_string + 'parsing' )
try:
content_update_package = HydrusSerialisable.CreateFromNetworkString( obj_string )
if not isinstance( content_update_package, HydrusData.ServerToClientContentUpdatePackage ):
raise Exception()
except:
self._ReportSyncProcessingError( path, 'did not parse' )
return
HydrusGlobals.client_controller.pub( 'splash_set_title_text', self._name + ' - ' + update_index_string + subupdate_index_string )
job_key.SetVariable( 'popup_text_1', update_index_string + subupdate_index_string + 'processing' )
( did_it_all, c_u_p_weight_processed ) = HydrusGlobals.client_controller.WriteSynchronous( 'content_update_package', self._service_key, content_update_package, job_key )
total_content_weight_processed += c_u_p_weight_processed
if not did_it_all:
processing_went_ok = False
break
HydrusGlobals.client_controller.WaitUntilPubSubsEmpty()
time.sleep( 1 )
if options[ 'pause_repo_sync' ]:
break
( i_paused, should_quit ) = job_key.WaitIfNeeded()
if should_quit:
break
if processing_went_ok:
job_key.SetVariable( 'popup_text_2', 'committing service updates' )
service_updates = [ service_update for service_update in service_update_package.IterateServiceUpdates() ]
service_updates.append( HydrusData.ServiceUpdate( HC.SERVICE_UPDATE_NEXT_PROCESSING_TIMESTAMP, service_update_package.GetNextBegin() ) )
service_keys_to_service_updates = { self._service_key : service_updates }
self.ProcessServiceUpdates( service_keys_to_service_updates )
HydrusGlobals.client_controller.Write( 'service_updates', service_keys_to_service_updates )
HydrusGlobals.client_controller.WaitUntilPubSubsEmpty()
job_key.SetVariable( 'popup_gauge_2', ( 0, 1 ) )
job_key.SetVariable( 'popup_text_2', '' )
num_updates_processed += 1
time.sleep( 0.1 )
job_key.DeleteVariable( 'popup_gauge_1' )
job_key.DeleteVariable( 'popup_text_2' )
job_key.DeleteVariable( 'popup_gauge_2' )
self.SyncThumbnails( job_key )
HydrusGlobals.client_controller.pub( 'splash_set_status_text', '' )
job_key.SetVariable( 'popup_title', 'repository synchronisation - ' + self._name + ' - finished' )
updates_text = HydrusData.ConvertIntToPrettyString( num_updates_downloaded ) + ' updates downloaded, ' + HydrusData.ConvertIntToPrettyString( num_updates_processed ) + ' updates processed'
if self._service_type == HC.TAG_REPOSITORY: content_text = HydrusData.ConvertIntToPrettyString( total_content_weight_processed ) + ' mappings added'
elif self._service_type == HC.FILE_REPOSITORY: content_text = HydrusData.ConvertIntToPrettyString( total_content_weight_processed ) + ' files added'
job_key.SetVariable( 'popup_text_1', updates_text + ', and ' + content_text )
HydrusData.Print( job_key.ToString() )
time.sleep( 3 )
job_key.Delete()
except Exception as e:
job_key.Cancel()
HydrusData.Print( traceback.format_exc() )
HydrusData.ShowText( 'Failed to update ' + self._name + ':' )
HydrusData.ShowException( e )
time.sleep( 3 )
finally:
HydrusGlobals.client_controller.pub( 'notify_new_pending' )
HydrusGlobals.client_controller.pub( 'notify_new_siblings_data' )
HydrusGlobals.client_controller.pub( 'notify_new_siblings_gui' )
HydrusGlobals.client_controller.pub( 'notify_new_parents' )
def SyncThumbnails( self, job_key ):
synced = not ( self.IsUpdateDueForDownload() or self.IsUpdateDueForProcessing() )
if self._service_type == HC.FILE_REPOSITORY and synced and self.CanDownload():
options = HydrusGlobals.client_controller.GetOptions()
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'reviewing service thumbnails' )
job_key.SetVariable( 'popup_text_1', 'reviewing service thumbnails' )
remote_thumbnail_hashes_i_should_have = HydrusGlobals.client_controller.Read( 'remote_thumbnail_hashes_i_should_have', self._service_key )
client_files_manager = HydrusGlobals.client_controller.GetClientFilesManager()
thumbnail_hashes_i_need = set()
for hash in remote_thumbnail_hashes_i_should_have:
if not client_files_manager.HaveFullSizeThumbnail( hash ):
thumbnail_hashes_i_need.add( hash )
if len( thumbnail_hashes_i_need ) > 0:
thumbnails = []
for ( i, hash ) in enumerate( thumbnail_hashes_i_need ):
if options[ 'pause_repo_sync' ]:
break
( i_paused, should_quit ) = job_key.WaitIfNeeded()
if should_quit:
break
job_key.SetVariable( 'popup_text_1', 'downloading thumbnail ' + HydrusData.ConvertValueRangeToPrettyString( i, len( thumbnail_hashes_i_need ) ) )
job_key.SetVariable( 'popup_gauge_1', ( i, len( thumbnail_hashes_i_need ) ) )
request_args = { 'hash' : hash.encode( 'hex' ) }
thumbnail = self.Request( HC.GET, 'thumbnail', request_args = request_args )
client_files_manager.AddFullSizeThumbnail( hash, thumbnail )
job_key.DeleteVariable( 'popup_gauge_1' )
class ServiceIPFS( ServiceRemote ):
def _ConvertMultihashToURLTree( self, name, size, multihash ):
api_base_url = self._GetAPIBaseURL()
links_url = api_base_url + 'object/links/' + multihash
response = ClientNetworking.RequestsGet( links_url )
links_json = response.json()
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 ):
credentials = self.GetCredentials()
( host, port ) = credentials.GetAddress()
api_base_url = 'http://' + host + ':' + str( port ) + '/api/v0/'
return api_base_url
def GetDaemonVersion( self ):
api_base_url = self._GetAPIBaseURL()
url = api_base_url + 'version'
response = ClientNetworking.RequestsGet( url )
j = response.json()
return j[ 'Version' ]
def ImportFile( self, multihash ):
def on_wx_select_tree( job_key, url_tree ):
import ClientGUIDialogs
with ClientGUIDialogs.DialogSelectFromURLTree( None, url_tree ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
urls = dlg.GetURLs()
if len( urls ) > 0:
HydrusGlobals.client_controller.CallToThread( ClientDownloading.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' )
HydrusGlobals.client_controller.pub( 'message', job_key )
url_tree = self._ConvertMultihashToURLTree( multihash, None, multihash )
if url_tree[0] == 'file':
url = url_tree[3]
HydrusGlobals.client_controller.CallToThread( ClientDownloading.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 )
HydrusGlobals.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 )
HydrusGlobals.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, ) = HydrusGlobals.client_controller.Read( 'media_results', ( hash, ) )
mime = media_result.GetMime()
result = HydrusGlobals.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 ) )
api_base_url = self._GetAPIBaseURL()
url = api_base_url + 'object/new?arg=unixfs-dir'
response = ClientNetworking.RequestsGet( url )
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.encode( 'hex' ) + HC.mime_ext_lookup[ mime ]
url = api_base_url + 'object/patch/add-link?arg=' + object_multihash + '&arg=' + filename + '&arg=' + multihash
response = ClientNetworking.RequestsGet( url )
directory_multihash = response.json()[ 'Hash' ]
url = api_base_url + 'pin/add?arg=' + directory_multihash
response = ClientNetworking.RequestsGet( url )
content_update_row = ( hashes, directory_multihash, note )
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_DIRECTORIES, HC.CONTENT_UPDATE_ADD, content_update_row ) ]
HydrusGlobals.client_controller.WriteSynchronous( 'content_updates', { self._service_key : content_updates } )
job_key.SetVariable( 'popup_text_1', 'done!' )
job_key.DeleteVariable( 'popup_gauge_1' )
multihash_prefix = self._info[ 'multihash_prefix' ]
text = 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 ]
api_base_url = self._GetAPIBaseURL()
url = api_base_url + 'add'
client_files_manager = HydrusGlobals.client_controller.GetClientFilesManager()
path = client_files_manager.GetFilePath( hash, mime )
files = { 'path' : ( hash.encode( 'hex' ), open( path, 'rb' ), mime_string ) }
response = ClientNetworking.RequestsPost( url, files = files )
j = response.json()
multihash = j[ 'Hash' ]
content_update_row = ( hash, multihash )
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_ADD, content_update_row ) ]
HydrusGlobals.client_controller.WriteSynchronous( 'content_updates', { self._service_key : content_updates } )
return multihash
def UnpinDirectory( self, multihash ):
api_base_url = self._GetAPIBaseURL()
url = api_base_url + 'pin/rm/' + multihash
ClientNetworking.RequestsGet( url )
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_DIRECTORIES, HC.CONTENT_UPDATE_DELETE, multihash ) ]
HydrusGlobals.client_controller.WriteSynchronous( 'content_updates', { self._service_key : content_updates } )
def UnpinFile( self, hash, multihash ):
api_base_url = self._GetAPIBaseURL()
url = api_base_url + 'pin/rm/' + multihash
try:
ClientNetworking.RequestsGet( url )
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 } ) ]
HydrusGlobals.client_controller.WriteSynchronous( 'content_updates', { self._service_key : content_updates } )
class Shortcuts( HydrusSerialisable.SerialisableBaseNamed ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUTS
SERIALISABLE_VERSION = 1
def __init__( self, name ):
HydrusSerialisable.SerialisableBaseNamed.__init__( self, name )
self._mouse_actions = {}
self._keyboard_actions = {}
def _ConvertActionToSerialisableAction( self, action ):
( service_key, data ) = action
if service_key is None:
return [ service_key, data ]
else:
serialisable_service_key = service_key.encode( 'hex' )
return [ serialisable_service_key, data ]
def _ConvertSerialisableActionToAction( self, serialisable_action ):
( serialisable_service_key, data ) = serialisable_action
if serialisable_service_key is None:
return ( serialisable_service_key, data ) # important to return tuple, as serialisable_action is likely a list
else:
service_key = serialisable_service_key.decode( 'hex' )
return ( service_key, data )
def _GetSerialisableInfo( self ):
serialisable_mouse_actions = []
for ( ( modifier, mouse_button ), action ) in self._mouse_actions.items():
serialisable_action = self._ConvertActionToSerialisableAction( action )
serialisable_mouse_actions.append( ( modifier, mouse_button, serialisable_action ) )
serialisable_keyboard_actions = []
for ( ( modifier, key ), action ) in self._keyboard_actions.items():
serialisable_action = self._ConvertActionToSerialisableAction( action )
serialisable_keyboard_actions.append( ( modifier, key, serialisable_action ) )
return ( serialisable_mouse_actions, serialisable_keyboard_actions )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( serialisable_mouse_actions, serialisable_keyboard_actions ) = serialisable_info
self._mouse_actions = {}
for ( modifier, mouse_button, serialisable_action ) in serialisable_mouse_actions:
action = self._ConvertSerialisableActionToAction( serialisable_action )
self._mouse_actions[ ( modifier, mouse_button ) ] = action
self._keyboard_actions = {}
for ( modifier, key, serialisable_action ) in serialisable_keyboard_actions:
action = self._ConvertSerialisableActionToAction( serialisable_action )
self._keyboard_actions[ ( modifier, key ) ] = action
def ClearActions( self ):
self._mouse_actions = {}
self._keyboard_actions = {}
def DeleteKeyboardAction( self, modifier, key ):
if ( modifier, key ) in self._keyboard_actions:
del self._keyboard_actions[ ( modifier, key ) ]
def DeleteMouseAction( self, modifier, mouse_button ):
if ( modifier, mouse_button ) in self._mouse_actions:
del self._mouse_actions[ ( modifier, mouse_button ) ]
def GetKeyboardAction( self, modifier, key ):
if ( modifier, key ) in self._keyboard_actions:
return self._keyboard_actions[ ( modifier, key ) ]
else:
return None
def GetMouseAction( self, modifier, mouse_button ):
if ( modifier, mouse_button ) in self._mouse_actions:
return self._mouse_actions[ ( modifier, mouse_button ) ]
else:
return None
def IterateKeyboardShortcuts( self ):
for ( ( modifier, key ), action ) in self._keyboard_actions.items(): yield ( ( modifier, key ), action )
def IterateMouseShortcuts( self ):
for ( ( modifier, mouse_button ), action ) in self._mouse_actions.items(): yield ( ( modifier, mouse_button ), action )
def SetKeyboardAction( self, modifier, key, action ):
self._keyboard_actions[ ( modifier, key ) ] = action
def SetMouseAction( self, modifier, mouse_button, action ):
self._mouse_actions[ ( modifier, mouse_button ) ] = action
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUTS ] = Shortcuts
def GetShortcutFromEvent( event ):
modifier = wx.ACCEL_NORMAL
if event.AltDown(): modifier = wx.ACCEL_ALT
elif event.CmdDown(): modifier = wx.ACCEL_CTRL
elif event.ShiftDown(): modifier = wx.ACCEL_SHIFT
key = event.KeyCode
return ( modifier, key )