Version 156

This commit is contained in:
Hydrus 2015-05-06 15:26:18 -05:00
parent 158a6f8f20
commit b44ff7d9b5
29 changed files with 411 additions and 344 deletions

0
export/.gitignore vendored
View File

View File

@ -8,6 +8,32 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 156</h3></li>
<ul>
<li>improved my build workflow in several ways, which should result in fewer orphan files and other build weirdnesses</li>
<li>some bad path usage in initialisation icon resizing has been moved to better temp paths</li>
<li>hitting page up or down on a manage tags dialog launched from the thumbnail grid no longer clears the current media</li>
<li>improved file permissions code across the program</li>
<li>fixed import folders daemon's test code for non-windows</li>
<li>fixed up some temporary file code that wasn't cleaning up those files when the application was about to close</li>
<li>fixed a newline parsing problem in copy/paste tags in the manage tags dialog</li>
<li>added tag cleaning to pasting in the manage tags dialog</li>
<li>added newline removal into standard tag cleaning process</li>
<li>fixed a server db bug that was stopping some accounts from being created</li>
<li>fixed some network session exception creation and catching</li>
<li>new popup messages should no longer steal focus in most circumstances</li>
<li>client should recover from serious popup message manager errors better</li>
<li>hover windows will now only pop up if their media viewer is the currently focused frame</li>
<li>hover windows will not hide until the mouse moves off them when flash or webm are underneath them</li>
<li>os x will no longer vanish media in the media viewer on an action like archive or inbox</li>
<li>fixed juddery media mouse dragging in linux</li>
<li>improved the way listbooks work to avoid a problem with clientdata in wx linux</li>
<li>export folder is gone as the default export location--now it is 'hydrus_export' under the current user's home dir</li>
<li>updated windows ffmpeg to latest version</li>
<li>fixed an important opencv dll conflict that was causing some gifs to render wrong in windows</li>
<li>shift focus media logic improved--shift initial thumbnail is now last image selected</li>
<li>shift selection will no longer deselect anything</li>
</ul>
<li><h3>version 155</h3></li>
<ul>
<li>fixed a frame seek error when looping long and/or large gifs with unusual palettes</li>

View File

@ -387,19 +387,26 @@ class ThumbnailCache( object ):
names = [ 'hydrus', 'flash', 'pdf', 'audio', 'video' ]
for name in names:
( os_file_handle, temp_path ) = HydrusFileHandling.GetTempPath()
try:
path = HC.STATIC_DIR + os.path.sep + name + '.png'
for name in names:
path = HC.STATIC_DIR + os.path.sep + name + '.png'
thumbnail = HydrusFileHandling.GenerateThumbnail( path, HC.options[ 'thumbnail_dimensions' ] )
with open( temp_path, 'wb' ) as f: f.write( thumbnail )
hydrus_bitmap = HydrusImageHandling.GenerateHydrusBitmap( temp_path )
self._special_thumbs[ name ] = hydrus_bitmap
thumbnail = HydrusFileHandling.GenerateThumbnail( path, HC.options[ 'thumbnail_dimensions' ] )
finally:
resized_path = HC.STATIC_DIR + os.path.sep + name + '_resized.png'
with open( resized_path, 'wb' ) as f: f.write( thumbnail )
hydrus_bitmap = HydrusImageHandling.GenerateHydrusBitmap( resized_path )
self._special_thumbs[ name ] = hydrus_bitmap
HydrusFileHandling.CleanUpTempPath( os_file_handle, temp_path )

View File

@ -525,8 +525,6 @@ class Controller( HydrusController.HydrusController ):
self.InitDB() # can't run on wx thread because we need event queue free to update splash text
# can't use HC.options, because it might not have initialised yet!
HC.options = wx.GetApp().Read( 'options' )
if HC.options[ 'password' ] is not None:

View File

@ -4879,28 +4879,6 @@ class DB( HydrusDB.HydrusDB ):
HydrusGlobals.pubsub.pub( 'splash_set_text', 'updating db to v' + HydrusData.ToString( version + 1 ) )
if version == 106:
self._c.execute( 'CREATE TABLE tag_censorship ( service_id INTEGER PRIMARY KEY REFERENCES services ON DELETE CASCADE, blacklist INTEGER_BOOLEAN, tags TEXT_YAML );' )
result = self._c.execute( 'SELECT service_id, blacklist, namespaces FROM namespace_blacklists;' ).fetchall()
for ( service_id, blacklist, namespaces ) in result:
tags = [ namespace + ':' for namespace in namespaces ]
if ':' in tags: # don't want to change ''!
tags.remove( ':' )
tags.append( '' )
self._c.execute( 'INSERT INTO tag_censorship ( service_id, blacklist, tags ) VALUES ( ?, ?, ? );', ( service_id, blacklist, tags ) )
self._c.execute( 'DROP TABLE namespace_blacklists;' )
if version == 108:
self._c.execute( 'CREATE TABLE processed_mappings ( service_id INTEGER REFERENCES services ON DELETE CASCADE, namespace_id INTEGER, tag_id INTEGER, hash_id INTEGER, status INTEGER, PRIMARY KEY( service_id, namespace_id, tag_id, hash_id, status ) );' )

View File

@ -163,11 +163,12 @@ def DAEMONCheckImportFolders():
try:
# make read only perms to make sure it isn't being written/downloaded right now
# try to get a write lock just to check it isn't being written to right now
os.chmod( path, stat.S_IREAD )
os.chmod( path, stat.S_IWRITE )
with open( path, 'ab' ) as f:
pass
with open( path, 'rb' ) as f_source:

View File

@ -120,6 +120,23 @@ def GenerateExportFilename( media, terms ):
return filename
def GetExportPath():
path = HC.options[ 'export_path' ]
if path is None:
path = os.path.expanduser( '~' ) + os.path.sep + 'hydrus_export'
if not os.path.exists( path ): os.mkdir( path )
path = os.path.normpath( path ) # converts slashes to backslashes for windows
path = HydrusData.ConvertPortablePathToAbsPath( path )
return path
def GetMediasTagCount( pool, tag_service_key = CC.COMBINED_TAG_SERVICE_KEY ):
tags_managers = []
@ -1062,14 +1079,11 @@ class Service( HydrusData.HydrusYAMLBase ):
except Exception as e:
if isinstance( e, HydrusExceptions.ForbiddenException ):
if isinstance( e, HydrusExceptions.SessionException ):
if HydrusData.ToString( e ) == 'Session not found!':
session_manager = wx.GetApp().GetManager( 'hydrus_sessions' )
session_manager.DeleteSessionKey( self._service_key )
session_manager = wx.GetApp().GetManager( 'hydrus_sessions' )
session_manager.DeleteSessionKey( self._service_key )
wx.GetApp().Write( 'service_updates', { self._service_key : [ HydrusData.ServiceUpdate( HC.SERVICE_UPDATE_ERROR, HydrusData.ToString( e ) ) ] } )

View File

@ -1,6 +1,7 @@
import ClientConstants as CC
import ClientData
import HydrusConstants as HC
import os
import wx
def GetClientDefaultOptions():
@ -10,7 +11,7 @@ def GetClientDefaultOptions():
options[ 'play_dumper_noises' ] = True
options[ 'default_sort' ] = 0
options[ 'default_collect' ] = None
options[ 'export_path' ] = 'export'
options[ 'export_path' ] = None
options[ 'hpos' ] = 400
options[ 'vpos' ] = 700
options[ 'exclude_deleted_files' ] = False

View File

@ -1064,11 +1064,6 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
with ClientGUIDialogsManage.DialogManageBoorus( self ) as dlg: dlg.ShowModal()
def _ManageContacts( self ):
with ClientGUIDialogsManage.DialogManageContacts( self ) as dlg: dlg.ShowModal()
def _ManageExportFolders( self ):
with ClientGUIDialogsManage.DialogManageExportFolders( self ) as dlg: dlg.ShowModal()
@ -1258,15 +1253,9 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
def _OpenExportFolder( self ):
export_path = HydrusData.ConvertPortablePathToAbsPath( HC.options[ 'export_path' ] )
export_path = ClientData.GetExportPath()
if export_path is None: wx.MessageBox( 'Export folder is missing or not set.' )
else:
export_path = os.path.normpath( export_path ) # windows complains about those forward slashes when launching from the command line
HydrusFileHandling.LaunchDirectory( export_path )
HydrusFileHandling.LaunchDirectory( export_path )
def _PauseSync( self, sync_type ):
@ -1912,7 +1901,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
elif command == 'manage_4chan_pass': self._Manage4chanPass()
elif command == 'manage_account_types': self._ManageAccountTypes( data )
elif command == 'manage_boorus': self._ManageBoorus()
elif command == 'manage_contacts': self._ManageContacts()
elif command == 'manage_export_folders': self._ManageExportFolders()
elif command == 'manage_imageboards': self._ManageImageboards()
elif command == 'manage_import_folders': self._ManageImportFolders()
@ -2193,13 +2181,16 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def Shutdown( self ):
self._message_manager.Hide()
try:
self._message_manager.Hide()
self._message_manager.CleanBeforeDestroy()
except: pass
self.Hide()
try: self._message_manager.CleanBeforeDestroy()
except: pass
for page in [ self._notebook.GetPage( i ) for i in range( self._notebook.GetPageCount() ) ]: page.CleanBeforeDestroy()
page = self._notebook.GetCurrentPage()

View File

@ -641,6 +641,9 @@ class Canvas( object ):
( new_size, new_position ) = self._GetMediaContainerSizeAndPosition()
if new_size != self._media_container.GetSize(): self._media_container.SetSize( new_size )
if HC.PLATFORM_OSX and new_position == self._media_container.GetPosition(): self._media_container.Refresh()
if new_position != self._media_container.GetPosition(): self._media_container.SetPosition( new_position )
@ -1303,13 +1306,8 @@ class CanvasFullscreenMediaList( ClientMedia.ListeningMediaList, CanvasWithDetai
( delta_x, delta_y ) = ( x - old_x, y - old_y )
try:
if HC.PLATFORM_OSX: raise Exception() # can't warppointer in os x
self.WarpPointer( old_x, old_y )
except: self._last_drag_coordinates = ( x, y )
if HC.PLATFORM_WINDOWS: self.WarpPointer( old_x, old_y )
else: self._last_drag_coordinates = ( x, y )
( old_delta_x, old_delta_y ) = self._total_drag_delta
@ -1342,7 +1340,7 @@ class CanvasFullscreenMediaList( ClientMedia.ListeningMediaList, CanvasWithDetai
if x > client_x - 20: better_x = client_x - 20
if y > client_y - 20: better_y = client_y - 20
if not HC.PLATFORM_OSX: # can't warppointer in os x
if HC.PLATFORM_WINDOWS:
self.WarpPointer( better_x, better_y )
@ -2494,13 +2492,13 @@ class FullscreenHoverFrame( wx.Frame ):
in_x = my_x <= mouse_x and mouse_x <= my_x + my_width
in_y = my_y <= mouse_y and mouse_y <= my_y + my_height
a_dialog_is_open = False
no_dialogs_open = True
tlps = wx.GetTopLevelWindows()
for tlp in tlps:
if isinstance( tlp, wx.Dialog ): a_dialog_is_open = True
if isinstance( tlp, wx.Dialog ): no_dialogs_open = False
mime = self._current_media.GetMime()
@ -2509,8 +2507,24 @@ class FullscreenHoverFrame( wx.Frame ):
mouse_over_important_media = ( ShouldHaveAnimationBar( self._current_media ) or mime in HC.VIDEO or mime == HC.APPLICATION_FLASH ) and self.GetParent().MouseIsOverMedia()
if in_position and not mouse_over_important_media and not a_dialog_is_open: self.Show()
else: self.Hide()
current_focus = wx.Window.FindFocus()
tlp = wx.GetTopLevelParent( current_focus )
my_parent_in_focus_tree = False
while tlp is not None:
if tlp == self.GetParent(): my_parent_in_focus_tree = True
tlp = tlp.GetParent()
ready_to_show = in_position and not mouse_over_important_media and no_dialogs_open and my_parent_in_focus_tree
ready_to_hide = not in_position or not no_dialogs_open or not my_parent_in_focus_tree
if ready_to_show: self.Show()
elif ready_to_hide: self.Hide()
@ -4058,13 +4072,8 @@ class RatingsFilterFrameNumerical( ClientGUICommon.FrameThatResizes ):
( delta_x, delta_y ) = ( x - old_x, y - old_y )
try:
if HC.PLATFORM_OSX: raise Exception() # can't warppointer in os x
self.WarpPointer( old_x, old_y )
except: self._last_drag_coordinates = ( x, y )
if HC.PLATFORM_WINDOWS: self.WarpPointer( old_x, old_y )
else: self._last_drag_coordinates = ( x, y )
( old_delta_x, old_delta_y ) = self._total_drag_delta
@ -4099,7 +4108,7 @@ class RatingsFilterFrameNumerical( ClientGUICommon.FrameThatResizes ):
if x > client_x - 20: better_x = client_x - 20
if y > client_y - 20: better_y = client_y - 20
if not HC.PLATFORM_OSX: # can't warppointer in os x
if HC.PLATFORM_WINDOWS:
self.WarpPointer( better_x, better_y )

View File

@ -1454,6 +1454,8 @@ class ListBook( wx.Panel ):
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._names_to_pages = {}
self._list_box = self.LB( self, style = wx.LB_SINGLE | wx.LB_SORT )
self._empty_panel = wx.Panel( self )
@ -1511,7 +1513,7 @@ class ListBook( wx.Panel ):
if selection == wx.NOT_FOUND: self._current_panel = self._empty_panel
else:
panel_info = self._list_box.GetClientData( selection )
panel_info = self._names_to_pages[ self._current_name ]
if type( panel_info ) == tuple:
@ -1523,12 +1525,12 @@ class ListBook( wx.Panel ):
self._panel_sizer.AddF( page, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self._list_box.SetClientData( selection, page )
self._names_to_pages[ self._current_name ] = page
self._RecalcListBoxWidth()
self._current_panel = self._list_box.GetClientData( selection )
self._current_panel = self._names_to_pages[ self._current_name ]
self._current_panel.Show()
@ -1551,7 +1553,9 @@ class ListBook( wx.Panel ):
self._panel_sizer.AddF( page, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self._list_box.Append( name, page )
self._list_box.Append( name )
self._names_to_pages[ name ] = page
self._RecalcListBoxWidth()
@ -1571,6 +1575,8 @@ class ListBook( wx.Panel ):
self._current_panel = self._empty_panel
self._names_to_pages = {}
self._list_box.Clear()
@ -1609,7 +1615,10 @@ class ListBook( wx.Panel ):
else: return self._current_panel
def GetNameToPageDict( self ): return { self._list_box.GetString( i ) : self._list_box.GetClientData( i ) for i in range( self._list_box.GetCount() ) if type( self._list_box.GetClientData( i ) ) != tuple }
def GetNameToPageDict( self ):
return self._names_to_pages
def NameExists( self, name, panel = None ): return self._list_box.FindString( name ) != wx.NOT_FOUND
@ -1626,7 +1635,7 @@ class ListBook( wx.Panel ):
elif previous_selection >= 0: self._Select( previous_selection )
else: self._Select( wx.NOT_FOUND )
panel_info = self._list_box.GetClientData( selection )
panel_info = self._names_to_pages[ self._current_name ]
if type( panel_info ) != tuple:
@ -1635,6 +1644,8 @@ class ListBook( wx.Panel ):
wx.CallAfter( panel_info.Destroy )
del self._names_to_pages[ self._current_name ]
self._list_box.Delete( selection )
self._RecalcListBoxWidth()
@ -1647,6 +1658,12 @@ class ListBook( wx.Panel ):
if self._current_name == name: self._current_name = new_name
page = self._names_to_pages[ name ]
del self._names_to_pages[ name ]
self._names_to_pages[ new_name ] = page
self._list_box.SetString( self._list_box.FindString( name ), new_name )
self._RecalcListBoxWidth()
@ -1681,13 +1698,13 @@ class ListBook( wx.Panel ):
def SelectPage( self, page ):
def SelectPage( self, page_to_select ):
for i in range( self._list_box.GetCount() ):
for ( name, page ) in self._names_to_pages.items():
if self._list_box.GetClientData( i ) == page:
if page == page_to_select:
self._Select( i )
self._Select( self._list_box.FindString( name ) )
return
@ -3640,8 +3657,6 @@ class PopupMessageManager( wx.Frame ):
parent.Bind( wx.EVT_SIZE, self.EventMove )
parent.Bind( wx.EVT_MOVE, self.EventMove )
self._SizeAndPositionAndShow()
HydrusGlobals.pubsub.sub( self, 'AddMessage', 'message' )
self._old_excepthook = sys.excepthook
@ -3705,7 +3720,21 @@ class PopupMessageManager( wx.Frame ):
num_messages_displayed = self._message_vbox.GetItemCount()
if num_messages_displayed > 0: self.Show()
if num_messages_displayed > 0:
current_focus = wx.Window.FindFocus()
tlp = wx.GetTopLevelParent( current_focus )
show_happened = self.Show()
if show_happened and tlp is not None:
self.Raise()
tlp.Raise()
else: self.Hide()
except:
@ -3802,7 +3831,7 @@ class PopupMessageManager( wx.Frame ):
message_window.Update()
self.MakeSureEverythingFits()
self._SizeAndPositionAndShow()
class RegexButton( wx.Button ):

View File

@ -4825,12 +4825,9 @@ class DialogSetupExport( Dialog ):
self._paths.Append( pretty_tuple, data_tuple )
if HC.options[ 'export_path' ] is not None:
abs_path = HydrusData.ConvertPortablePathToAbsPath( HC.options[ 'export_path' ] )
if abs_path is not None: self._directory_picker.SetPath( abs_path )
export_path = ClientData.GetExportPath()
self._directory_picker.SetPath( export_path )
self._zip_name.SetValue( 'archive name.zip' )
@ -5000,8 +4997,6 @@ class DialogSetupExport( Dialog ):
source_path = ClientFiles.GetFilePath( hash, mime )
if os.path.exists( path ): os.chmod( path, stat.S_IWRITE )
shutil.copy( source_path, path )
except:

View File

@ -3156,6 +3156,8 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
def ArrangeControls():
vbox = wx.BoxSizer( wx.VERTICAL )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
@ -3172,7 +3174,12 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
gridbox.AddF( wx.StaticText( self._file_page, label = 'Thumbnail height: ' ), CC.FLAGS_MIXED )
gridbox.AddF( self._thumbnail_height, CC.FLAGS_MIXED )
self._file_page.SetSizer( gridbox )
text = 'If you set the default export directory blank, the client will use \'hydrus_export\' under the current user\'s home directory.'
vbox.AddF( wx.StaticText( self._file_page, label = text ), CC.FLAGS_CENTER )
vbox.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self._file_page.SetSizer( vbox )
#
@ -7710,11 +7717,14 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
def EventNext( self, event ):
self._CommitCurrentChanges()
self._ClearPanels()
HydrusGlobals.pubsub.pub( 'canvas_show_next', self._canvas_key )
if self._canvas_key is not None:
self._CommitCurrentChanges()
self._ClearPanels()
HydrusGlobals.pubsub.pub( 'canvas_show_next', self._canvas_key )
def EventOK( self, event ):
@ -7725,11 +7735,14 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
def EventPrevious( self, event ):
self._CommitCurrentChanges()
self._ClearPanels()
HydrusGlobals.pubsub.pub( 'canvas_show_previous', self._canvas_key )
if self._canvas_key is not None:
self._CommitCurrentChanges()
self._ClearPanels()
HydrusGlobals.pubsub.pub( 'canvas_show_previous', self._canvas_key )
def EventServiceChanged( self, event ):
@ -7754,63 +7767,6 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
def __init__( self, parent, file_service_key, tag_service_key, media ):
def InitialiseControls():
self._tags_box_sorter = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'tags' )
self._tags_box = ClientGUICommon.ListBoxTagsSelectionTagsDialog( self._tags_box_sorter, self.AddTag )
self._tags_box_sorter.SetTagsBox( self._tags_box )
self._show_deleted_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'show deleted' )
self._show_deleted_checkbox.Bind( wx.EVT_CHECKBOX, self.EventShowDeleted )
self._tags_box_sorter.AddF( self._show_deleted_checkbox, CC.FLAGS_LONE_BUTTON )
self._add_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddTag, self._file_service_key, self._tag_service_key )
self._modify_mappers = wx.Button( self, label = 'Modify mappers' )
self._modify_mappers.Bind( wx.EVT_BUTTON, self.EventModify )
self._copy_tags = wx.Button( self, label = 'copy tags' )
self._copy_tags.Bind( wx.EVT_BUTTON, self.EventCopyTags )
self._paste_tags = wx.Button( self, label = 'paste tags' )
self._paste_tags.Bind( wx.EVT_BUTTON, self.EventPasteTags )
def PopulateControls():
self._tags_box.ChangeTagRepository( self._tag_service_key )
self.SetMedia( media )
def ArrangeControls():
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
if self._i_am_local_tag_service: self._modify_mappers.Hide()
else:
if not self._account.HasPermission( HC.MANAGE_USERS ): self._modify_mappers.Hide()
copy_paste_hbox = wx.BoxSizer( wx.HORIZONTAL )
copy_paste_hbox.AddF( self._copy_tags, CC.FLAGS_MIXED )
copy_paste_hbox.AddF( self._paste_tags, CC.FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._tags_box_sorter, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._add_tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( copy_paste_hbox, CC.FLAGS_BUTTON_SIZER )
vbox.AddF( self._modify_mappers, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( vbox )
wx.Panel.__init__( self, parent )
self._file_service_key = file_service_key
@ -7826,11 +7782,53 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
except: self._account = HydrusData.GetUnknownAccount()
InitialiseControls()
self._tags_box_sorter = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'tags' )
PopulateControls()
self._tags_box = ClientGUICommon.ListBoxTagsSelectionTagsDialog( self._tags_box_sorter, self.AddTag )
ArrangeControls()
self._tags_box_sorter.SetTagsBox( self._tags_box )
self._show_deleted_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'show deleted' )
self._show_deleted_checkbox.Bind( wx.EVT_CHECKBOX, self.EventShowDeleted )
self._tags_box_sorter.AddF( self._show_deleted_checkbox, CC.FLAGS_LONE_BUTTON )
self._add_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddTag, self._file_service_key, self._tag_service_key )
self._modify_mappers = wx.Button( self, label = 'Modify mappers' )
self._modify_mappers.Bind( wx.EVT_BUTTON, self.EventModify )
self._copy_tags = wx.Button( self, label = 'copy tags' )
self._copy_tags.Bind( wx.EVT_BUTTON, self.EventCopyTags )
self._paste_tags = wx.Button( self, label = 'paste tags' )
self._paste_tags.Bind( wx.EVT_BUTTON, self.EventPasteTags )
self._tags_box.ChangeTagRepository( self._tag_service_key )
self.SetMedia( media )
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
if self._i_am_local_tag_service: self._modify_mappers.Hide()
else:
if not self._account.HasPermission( HC.MANAGE_USERS ): self._modify_mappers.Hide()
copy_paste_hbox = wx.BoxSizer( wx.HORIZONTAL )
copy_paste_hbox.AddF( self._copy_tags, CC.FLAGS_MIXED )
copy_paste_hbox.AddF( self._paste_tags, CC.FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._tags_box_sorter, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._add_tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( copy_paste_hbox, CC.FLAGS_BUTTON_SIZER )
vbox.AddF( self._modify_mappers, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( vbox )
def _AddTag( self, tag, only_add = False ):
@ -7919,23 +7917,6 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
def SetMedia( self, media ):
self._content_updates = []
if media is None: media = []
self._hashes = { hash for hash in itertools.chain.from_iterable( ( m.GetHashes() for m in media ) ) }
if len( self._hashes ) > 0: media_results = wx.GetApp().Read( 'media_results', self._file_service_key, self._hashes )
else: media_results = []
# this should now be a nice clean copy of the original media
self._media = [ ClientMedia.MediaSingleton( media_result ) for media_result in media_results ]
self._tags_box.SetTagsByMedia( self._media )
def EventCopyTags( self, event ):
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = ClientData.GetMediasTagCount( self._media, self._tag_service_key )
@ -7973,7 +7954,9 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
try:
tags = text.split( os.linesep )
tags = HydrusData.DeserialisePrettyTags( text )
tags = HydrusTags.CleanTags( tags )
for tag in tags: self._AddTag( tag, only_add = True )
@ -7993,6 +7976,23 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
def HasChanges( self ): return len( self._content_updates ) > 0
def SetMedia( self, media ):
self._content_updates = []
if media is None: media = []
self._hashes = { hash for hash in itertools.chain.from_iterable( ( m.GetHashes() for m in media ) ) }
if len( self._hashes ) > 0: media_results = wx.GetApp().Read( 'media_results', self._file_service_key, self._hashes )
else: media_results = []
# this should now be a nice clean copy of the original media
self._media = [ ClientMedia.MediaSingleton( media_result ) for media_result in media_results ]
self._tags_box.SetTagsByMedia( self._media )
def SetTagBoxFocus( self ):
self._add_tag_box.SetFocus()

View File

@ -344,37 +344,39 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
if self._focussed_media == media: self._SetFocussedMedia( None )
self._shift_focussed_media = None
else:
self._DeselectSelect( (), ( media, ) )
if self._focussed_media is None: self._SetFocussedMedia( media )
self._shift_focussed_media = media
self._shift_focussed_media = None
elif shift and self._focussed_media is not None:
if self._shift_focussed_media is None: self._shift_focussed_media = self._focussed_media
elif shift and self._shift_focussed_media is not None:
start_index = self._sorted_media.index( self._shift_focussed_media )
end_index = self._sorted_media.index( media )
if start_index < end_index: media_i_want_selected_at_the_end = set( self._sorted_media[ start_index : end_index + 1 ] )
else: media_i_want_selected_at_the_end = set( self._sorted_media[ end_index : start_index + 1 ] )
if start_index < end_index: media_to_select = set( self._sorted_media[ start_index : end_index + 1 ] )
else: media_to_select = set( self._sorted_media[ end_index : start_index + 1 ] )
self._DeselectSelect( self._selected_media - media_i_want_selected_at_the_end, media_i_want_selected_at_the_end - self._selected_media )
self._DeselectSelect( (), media_to_select )
self._SetFocussedMedia( media )
self._shift_focussed_media = media
else:
if not media.IsSelected(): self._DeselectSelect( self._selected_media, ( media, ) )
else: self._PublishSelectionChange()
self._SetFocussedMedia( media )
self._shift_focussed_media = None
self._shift_focussed_media = media

View File

@ -49,7 +49,7 @@ options = {}
# Misc
NETWORK_VERSION = 15
SOFTWARE_VERSION = 155
SOFTWARE_VERSION = 156
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -490,6 +490,14 @@ def DebugPrint( debug_info ):
sys.stdout.flush()
sys.stderr.flush()
def DeserialisePrettyTags( text ):
text = text.replace( '\r', '' )
tags = text.split( '\n' )
return tags
def GetEmptyDataDict():
data = collections.defaultdict( default_dict_list )

View File

@ -48,22 +48,17 @@ def ConvertAbsPathToPortablePath( abs_path ):
def CleanUpTempPath( os_file_handle, temp_path ):
def clean():
os.close( os_file_handle )
try: os.remove( temp_path )
except OSError:
os.close( os_file_handle )
gc.collect()
try: os.remove( temp_path )
except OSError:
gc.collect()
try: os.remove( temp_path )
except OSError: print( 'The client could not delete the temporary file ' + temp_path )
except OSError: print( 'The client could not delete the temporary file ' + temp_path )
wx.CallAfter( clean )
def CopyFileLikeToFileLike( f_source, f_dest ):
for block in HydrusData.ReadFileLikeAsBlocks( f_source, 65536 ): f_dest.write( block )

View File

@ -541,6 +541,7 @@ class HTTPConnection( object ):
if response.status == 401: raise HydrusExceptions.PermissionException( parsed_response )
elif response.status == 403: raise HydrusExceptions.ForbiddenException( parsed_response )
elif response.status == 404: raise HydrusExceptions.NotFoundException( parsed_response )
elif response.status == 419: raise HydrusExceptions.SessionException( parsed_response )
elif response.status == 426: raise HydrusExceptions.NetworkVersionException( parsed_response )
elif response.status in ( 500, 501, 502, 503 ): raise Exception( parsed_response )
else: raise Exception( parsed_response )

View File

@ -423,7 +423,7 @@ class HydrusResourceCommand( Resource ):
elif failure.type == HydrusExceptions.ForbiddenException: response_context = ResponseContext( 403, mime = default_mime, body = default_encoding( failure.value ) )
elif failure.type == HydrusExceptions.NotFoundException: response_context = ResponseContext( 404, mime = default_mime, body = default_encoding( failure.value ) )
elif failure.type == HydrusExceptions.NetworkVersionException: response_context = ResponseContext( 426, mime = default_mime, body = default_encoding( failure.value ) )
elif failure.type == HydrusExceptions.SessionException: response_context = ResponseContext( 403, mime = default_mime, body = default_encoding( failure.value ) )
elif failure.type == HydrusExceptions.SessionException: response_context = ResponseContext( 419, mime = default_mime, body = default_encoding( failure.value ) )
else:
print( failure.getTraceback() )

View File

@ -51,7 +51,7 @@ class HydrusMessagingSessionManagerServer( object ):
del self._service_keys_to_sessions[ service_key ][ session_key ]
raise HydrusExceptions.SessionException( 'Session expired!' )
raise HydrusExceptions.SessionException( 'Session expired! Try again!' )
return ( account.GetAccountKey(), name )
@ -190,7 +190,7 @@ class HydrusSessionManagerServer( object ):
else: return account
raise HydrusExceptions.SessionException()
raise HydrusExceptions.SessionException( 'Did not find that session! Try again!' )

View File

@ -916,6 +916,9 @@ def CleanTag( tag ):
tag = HydrusData.ToString( tag )
tag.replace( '\r', '' )
tag.replace( '\n', '' )
tag = re.sub( '[\\s]+', ' ', tag, flags = re.UNICODE ) # turns multiple spaces into single spaces
tag = re.sub( '\\s\\Z', '', tag, flags = re.UNICODE ) # removes space at the end
@ -943,8 +946,3 @@ def CleanTags( tags ):
return clean_tags

View File

@ -2280,6 +2280,20 @@ class DB( HydrusDB.HydrusDB ):
if version == 155:
results = self._c.execute( 'SELECT service_id, account_type_id, account_type FROM account_types;' ).fetchall()
for ( service_id, account_type_id, account_type ) in results:
title = account_type.GetTitle()
self._c.execute( 'UPDATE account_types SET title = ? WHERE service_id = ? AND account_type_id = ?;', ( title, service_id, account_type_id ) )
print( 'The server has updated to version ' + str( version + 1 ) )
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )

View File

@ -2,6 +2,8 @@ import ClientDaemons
import collections
import HydrusConstants as HC
import os
import shutil
import stat
import TestConstants
import tempfile
import unittest
@ -16,119 +18,121 @@ class TestDaemons( unittest.TestCase ):
test_dir = tempfile.mkdtemp()
if not os.path.exists( test_dir ): os.mkdir( test_dir )
with open( test_dir + os.path.sep + '1', 'wb' ) as f: f.write( TestConstants.tinest_gif )
with open( test_dir + os.path.sep + '2', 'wb' ) as f: f.write( TestConstants.tinest_gif )
with open( test_dir + os.path.sep + '3', 'wb' ) as f: f.write( TestConstants.tinest_gif )
with open( test_dir + os.path.sep + '4', 'wb' ) as f: f.write( 'blarg' ) # broken
with open( test_dir + os.path.sep + '5', 'wb' ) as f: f.write( TestConstants.tinest_gif ) # previously failed for whatever reason
#
path = test_dir
details = {}
details[ 'type' ] = HC.IMPORT_FOLDER_TYPE_SYNCHRONISE
details[ 'cached_imported_paths' ] = { test_dir + os.path.sep + '2' }
details[ 'failed_imported_paths' ] = { test_dir + os.path.sep + '5' }
details[ 'local_tag' ] = 'local tag'
details[ 'last_checked' ] = HydrusData.GetNow() - 1500
details[ 'check_period' ] = 1000
old_details = dict( details )
wx.GetApp().SetRead( 'import_folders', { path : details } )
ClientDaemons.DAEMONCheckImportFolders()
#(('C:\\code\\Hydrus\\temp\\7baa9a818a14b7a9cbefb04c16bdc45ac651eb7400c1996e66e2efeef9e3ee5d',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
#(('C:\\code\\Hydrus\\temp\\e0dbdcb1a13c0565ffb73f2f497528adbe1703ca1dfc69680202487187b9fcfa',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
#(('C:\\code\\Hydrus\\temp\\182c4eecf2a5b4dfc8b74813bcff5d967ed53d92a982d8ae18520e1504fa5902',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
import_file = wx.GetApp().GetWrite( 'import_file' )
self.assertEqual( len( import_file ), 3 )
expected_tag_part = { 'service_keys_to_tags' : { ClientConstants.LOCAL_TAG_SERVICE_KEY : set( [ 'local tag' ] ) } }
( one, two, three ) = import_file
( temp_path, tag_part ) = one
self.assertEqual( tag_part, expected_tag_part )
( temp_path, tag_part ) = two
self.assertEqual( tag_part, expected_tag_part )
( temp_path, tag_part ) = three
self.assertEqual( tag_part, expected_tag_part )
# I need to expand tests here with the new file system
[ ( ( updated_path, updated_details ), kwargs ) ] = wx.GetApp().GetWrite( 'import_folder' )
self.assertEqual( path, updated_path )
self.assertEqual( updated_details[ 'type' ], old_details[ 'type' ] )
self.assertEqual( updated_details[ 'cached_imported_paths' ], { test_dir + os.path.sep + '1', test_dir + os.path.sep + '2', test_dir + os.path.sep + '3' } )
self.assertEqual( updated_details[ 'failed_imported_paths' ], { test_dir + os.path.sep + '4', test_dir + os.path.sep + '5' } )
self.assertEqual( updated_details[ 'local_tag' ], old_details[ 'local_tag' ] )
self.assertGreater( updated_details[ 'last_checked' ], old_details[ 'last_checked' ] )
self.assertEqual( updated_details[ 'check_period' ], old_details[ 'check_period' ] )
#
path = test_dir
details = {}
details[ 'type' ] = HC.IMPORT_FOLDER_TYPE_DELETE
details[ 'cached_imported_paths' ] = set()
details[ 'failed_imported_paths' ] = { test_dir + os.path.sep + '5' }
details[ 'local_tag' ] = 'local tag'
details[ 'last_checked' ] = HydrusData.GetNow() - 1500
details[ 'check_period' ] = 1000
old_details = dict( details )
wx.GetApp().SetRead( 'import_folders', { path : details } )
ClientDaemons.DAEMONCheckImportFolders()
# improve these tests as above
# old entries
#(('GIF89a\x01\x00\x01\x00\x00\xff\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x00;',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
#(('GIF89a\x01\x00\x01\x00\x00\xff\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x00;',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
#(('GIF89a\x01\x00\x01\x00\x00\xff\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x00;',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
#(('blarg',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
import_file = wx.GetApp().GetWrite( 'import_file' )
[ ( ( updated_path, updated_details ), kwargs ) ] = wx.GetApp().GetWrite( 'import_folder' )
self.assertEqual( path, updated_path )
self.assertEqual( updated_details[ 'type' ], old_details[ 'type' ] )
self.assertEqual( updated_details[ 'cached_imported_paths' ], set() )
self.assertEqual( updated_details[ 'failed_imported_paths' ], { test_dir + os.path.sep + '4', test_dir + os.path.sep + '5' } )
self.assertEqual( updated_details[ 'local_tag' ], old_details[ 'local_tag' ] )
self.assertGreater( updated_details[ 'last_checked' ], old_details[ 'last_checked' ] )
self.assertEqual( updated_details[ 'check_period' ], old_details[ 'check_period' ] )
self.assertTrue( not os.path.exists( test_dir + os.path.sep + '1' ) )
self.assertTrue( not os.path.exists( test_dir + os.path.sep + '2' ) )
self.assertTrue( not os.path.exists( test_dir + os.path.sep + '3' ) )
self.assertTrue( os.path.exists( test_dir + os.path.sep + '4' ) )
self.assertTrue( os.path.exists( test_dir + os.path.sep + '5' ) )
os.remove( test_dir + os.path.sep + '4' )
os.remove( test_dir + os.path.sep + '5' )
os.rmdir( test_dir )
try:
if not os.path.exists( test_dir ): os.mkdir( test_dir )
with open( test_dir + os.path.sep + '0', 'wb' ) as f: f.write( TestConstants.tinest_gif )
with open( test_dir + os.path.sep + '1', 'wb' ) as f: f.write( TestConstants.tinest_gif ) # previously imported
with open( test_dir + os.path.sep + '2', 'wb' ) as f: f.write( TestConstants.tinest_gif )
with open( test_dir + os.path.sep + '3', 'wb' ) as f: f.write( 'blarg' ) # broken
with open( test_dir + os.path.sep + '4', 'wb' ) as f: f.write( 'blarg' ) # previously failed
#
path = test_dir
details = {}
details[ 'type' ] = HC.IMPORT_FOLDER_TYPE_SYNCHRONISE
details[ 'cached_imported_paths' ] = { test_dir + os.path.sep + '1' }
details[ 'failed_imported_paths' ] = { test_dir + os.path.sep + '4' }
details[ 'local_tag' ] = 'local tag'
details[ 'last_checked' ] = HydrusData.GetNow() - 1500
details[ 'check_period' ] = 1000
old_details = dict( details )
wx.GetApp().SetRead( 'import_folders', { path : details } )
ClientDaemons.DAEMONCheckImportFolders()
#(('C:\\code\\Hydrus\\temp\\7baa9a818a14b7a9cbefb04c16bdc45ac651eb7400c1996e66e2efeef9e3ee5d',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
#(('C:\\code\\Hydrus\\temp\\e0dbdcb1a13c0565ffb73f2f497528adbe1703ca1dfc69680202487187b9fcfa',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
#(('C:\\code\\Hydrus\\temp\\182c4eecf2a5b4dfc8b74813bcff5d967ed53d92a982d8ae18520e1504fa5902',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
import_file = wx.GetApp().GetWrite( 'import_file' )
self.assertEqual( len( import_file ), 3 )
expected_tag_part = { 'service_keys_to_tags' : { ClientConstants.LOCAL_TAG_SERVICE_KEY : set( [ 'local tag' ] ) } }
( one, two, three ) = import_file
( temp_path, tag_part ) = one
self.assertEqual( tag_part, expected_tag_part )
( temp_path, tag_part ) = two
self.assertEqual( tag_part, expected_tag_part )
( temp_path, tag_part ) = three
self.assertEqual( tag_part, expected_tag_part )
# I need to expand tests here with the new file system
[ ( ( updated_path, updated_details ), kwargs ) ] = wx.GetApp().GetWrite( 'import_folder' )
self.assertEqual( path, updated_path )
self.assertEqual( updated_details[ 'type' ], old_details[ 'type' ] )
self.assertEqual( updated_details[ 'cached_imported_paths' ], { test_dir + os.path.sep + '0', test_dir + os.path.sep + '1', test_dir + os.path.sep + '2' } )
self.assertEqual( updated_details[ 'failed_imported_paths' ], { test_dir + os.path.sep + '3', test_dir + os.path.sep + '4' } )
self.assertEqual( updated_details[ 'local_tag' ], old_details[ 'local_tag' ] )
self.assertGreater( updated_details[ 'last_checked' ], old_details[ 'last_checked' ] )
self.assertEqual( updated_details[ 'check_period' ], old_details[ 'check_period' ] )
#
path = test_dir
details = {}
details[ 'type' ] = HC.IMPORT_FOLDER_TYPE_DELETE
details[ 'cached_imported_paths' ] = set()
details[ 'failed_imported_paths' ] = { test_dir + os.path.sep + '4' }
details[ 'local_tag' ] = 'local tag'
details[ 'last_checked' ] = HydrusData.GetNow() - 1500
details[ 'check_period' ] = 1000
old_details = dict( details )
wx.GetApp().SetRead( 'import_folders', { path : details } )
ClientDaemons.DAEMONCheckImportFolders()
# improve these tests as above
# old entries
#(('GIF89a\x01\x00\x01\x00\x00\xff\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x00;',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
#(('GIF89a\x01\x00\x01\x00\x00\xff\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x00;',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
#(('GIF89a\x01\x00\x01\x00\x00\xff\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x00;',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
#(('blarg',), {'service_keys_to_tags': {HC.LOCAL_TAG_SERVICE_KEY: set(['local tag'])}})
import_file = wx.GetApp().GetWrite( 'import_file' )
[ ( ( updated_path, updated_details ), kwargs ) ] = wx.GetApp().GetWrite( 'import_folder' )
self.assertEqual( path, updated_path )
self.assertEqual( updated_details[ 'type' ], old_details[ 'type' ] )
self.assertEqual( updated_details[ 'cached_imported_paths' ], set() )
self.assertEqual( updated_details[ 'failed_imported_paths' ], { test_dir + os.path.sep + '3', test_dir + os.path.sep + '4' } )
self.assertEqual( updated_details[ 'local_tag' ], old_details[ 'local_tag' ] )
self.assertGreater( updated_details[ 'last_checked' ], old_details[ 'last_checked' ] )
self.assertEqual( updated_details[ 'check_period' ], old_details[ 'check_period' ] )
self.assertTrue( not os.path.exists( test_dir + os.path.sep + '0' ) )
self.assertTrue( not os.path.exists( test_dir + os.path.sep + '1' ) )
self.assertTrue( not os.path.exists( test_dir + os.path.sep + '2' ) )
self.assertTrue( os.path.exists( test_dir + os.path.sep + '3' ) )
self.assertTrue( os.path.exists( test_dir + os.path.sep + '4' ) )
finally:
shutil.rmtree( test_dir )

View File

@ -49,8 +49,6 @@ class TestClientDB( unittest.TestCase ):
HC.CLIENT_FILES_DIR = HC.DB_DIR + os.path.sep + 'client_files'
HC.CLIENT_THUMBNAILS_DIR = HC.DB_DIR + os.path.sep + 'client_thumbnails'
if not os.path.exists( HC.DB_DIR ): os.mkdir( HC.DB_DIR )
self._db = ClientDB.DB()
threading.Thread( target = self._db.MainLoop, name = 'Database Main Loop' ).start()
@ -71,7 +69,7 @@ class TestClientDB( unittest.TestCase ):
except: pass
if os.path.exists( HC.DB_DIR ): shutil.rmtree( HC.DB_DIR, onerror = make_temp_files_deletable )
shutil.rmtree( HC.DB_DIR, onerror = make_temp_files_deletable )
HC.DB_DIR = self._old_db_dir
HC.CLIENT_FILES_DIR = self._old_client_files_dir
@ -1152,8 +1150,6 @@ class TestServerDB( unittest.TestCase ):
HC.SERVER_FILES_DIR = HC.DB_DIR + os.path.sep + 'server_files'
HC.SERVER_THUMBNAILS_DIR = HC.DB_DIR + os.path.sep + 'server_thumbnails'
if not os.path.exists( HC.DB_DIR ): os.mkdir( HC.DB_DIR )
self._db = ServerDB.DB()
threading.Thread( target = self._db.MainLoop, name = 'Database Main Loop' ).start()
@ -1174,7 +1170,7 @@ class TestServerDB( unittest.TestCase ):
except: pass
if os.path.exists( HC.DB_DIR ): shutil.rmtree( HC.DB_DIR, onerror = make_temp_files_deletable )
shutil.rmtree( HC.DB_DIR, onerror = make_temp_files_deletable )
HC.DB_DIR = self._old_db_dir
HC.SERVER_FILES_DIR = self._old_server_files_dir

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB