Version 222
This commit is contained in:
parent
e5d0e791d6
commit
f478efd48c
|
@ -8,6 +8,31 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 222</h3></li>
|
||||
<ul>
|
||||
<li>created a 'raw url' downloader page that just downloads urls and tries to import the result. it has a 'paste urls' button to make mass import of a list of urls easy</li>
|
||||
<li>fixed an options update bug when updating to v221 from any version before v220</li>
|
||||
<li>added support for 'ftypqt' quicktime (usually .mov) video</li>
|
||||
<li>embed button now uses system gui colours</li>
|
||||
<li>embed button now puts the thumbnail of the media, if one exists, behind the 'play' button</li>
|
||||
<li>sped up an inefficient existing mapping check that was slowing new pending mappings for popular tags</li>
|
||||
<li>'last session' will no longer be listed on the gui session delete menu</li>
|
||||
<li>cleaned up the main gui's initialisation events--a sizing bug often triggered after system reboot may be fixed</li>
|
||||
<li>popup messages are initialised in a safer way</li>
|
||||
<li>popup messages are dismissed in a safer way</li>
|
||||
<li>popup messages will hide/restore themselves more reliably when the main gui window is minimised/restored</li>
|
||||
<li>the pending popup message queue is now regularly purged of already deleted messages</li>
|
||||
<li>new popup messages will no longer raise the main gui window to the top</li>
|
||||
<li>subscription http errors during the gallery sync phase are now caught and handled gracefully, with exact error text written quietly to the log</li>
|
||||
<li>network timeouts during successful response read are caught and converted to a hydrus network exception that will be caught and handled more reliably up the chain</li>
|
||||
<li>the client's upnp daemon will now silence upnp mapping errors that are due to the router being too busy or full or any other unknown errors. a simple statement about the error and an instruction to explore the problem with the manual upnp manager will be written to the log</li>
|
||||
<li>finished flexgrid refactoring</li>
|
||||
<li>the new automatic flexgrid creation detects subsizers and lines them up more accurately with standard controls</li>
|
||||
<li>wrapped the different sections of the 'speed and memory' options panel into staticboxes</li>
|
||||
<li>wraped the misc crap up top the 'tags' options panel into a neater staticbox</li>
|
||||
<li>taglists with unusual tags will copy them more reliably and present fewer invalid menu options</li>
|
||||
<li>misc layout fixes</li>
|
||||
</ul>
|
||||
<li><h3>version 221</h3></li>
|
||||
<ul>
|
||||
<li>fixed the 8chan thread parser for the new sha256 file urls. legacy links should still work!</li>
|
||||
|
|
|
@ -135,6 +135,7 @@ FLAGS_BUTTON_SIZER = wx.SizerFlags( 0 ).Align( wx.ALIGN_RIGHT )
|
|||
FLAGS_LONE_BUTTON = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_RIGHT )
|
||||
|
||||
FLAGS_VCENTER = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
|
||||
FLAGS_SIZER_VCENTER = wx.SizerFlags( 0 ).Align( wx.ALIGN_CENTRE_VERTICAL )
|
||||
FLAGS_VCENTER_EXPAND_DEPTH_ONLY = wx.SizerFlags( 2 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
|
||||
|
||||
DAY = 0
|
||||
|
@ -203,6 +204,7 @@ else:
|
|||
|
||||
media_viewer_capabilities[ HC.APPLICATION_PDF ] = no_support
|
||||
media_viewer_capabilities[ HC.VIDEO_FLV ] = animated_full_support
|
||||
media_viewer_capabilities[ HC.VIDEO_MOV ] = animated_full_support
|
||||
media_viewer_capabilities[ HC.VIDEO_MP4 ] = animated_full_support
|
||||
media_viewer_capabilities[ HC.VIDEO_MKV ] = animated_full_support
|
||||
media_viewer_capabilities[ HC.VIDEO_WEBM ] = animated_full_support
|
||||
|
|
|
@ -944,10 +944,9 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
self._app = wx.App()
|
||||
|
||||
self._app.locale = wx.Locale( wx.LANGUAGE_DEFAULT ) # Very important
|
||||
self._app.locale = wx.Locale( wx.LANGUAGE_DEFAULT ) # Very important to init this here and keep it non garbage collected
|
||||
|
||||
# I have had this as 'suppress' before
|
||||
# The default is to create exceptions, and since this stuff is usually pissy locale/missing parent stuff, we don't want to kill the boot
|
||||
self._app.SetAssertMode( wx.PYAPP_ASSERT_EXCEPTION )
|
||||
|
||||
HydrusData.Print( 'booting controller...' )
|
||||
|
|
|
@ -8174,7 +8174,13 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
for ( namespace_id, tag_id, hash_ids ) in pending_mappings_ids:
|
||||
|
||||
existing_current_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM ' + current_mappings_table_name + ' WHERE namespace_id = ? AND tag_id = ?;', ( namespace_id, tag_id ) ) }
|
||||
self._c.execute( 'CREATE TABLE mem.temp_pending_hash_ids ( hash_id INTEGER );' )
|
||||
|
||||
self._c.executemany( 'INSERT INTO temp_pending_hash_ids ( hash_id ) VALUES ( ? );', ( ( hash_id, ) for hash_id in hash_ids ) )
|
||||
|
||||
existing_current_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM temp_pending_hash_ids, ' + current_mappings_table_name + ' USING ( hash_id ) WHERE namespace_id = ? AND tag_id = ?;', ( namespace_id, tag_id ) ) }
|
||||
|
||||
self._c.execute( 'DROP TABLE mem.temp_pending_hash_ids;' )
|
||||
|
||||
valid_hash_ids = set( hash_ids ).difference( existing_current_hash_ids )
|
||||
|
||||
|
|
|
@ -405,7 +405,20 @@ def DAEMONUPnP( controller ):
|
|||
|
||||
duration = 3600
|
||||
|
||||
HydrusNATPunch.AddUPnPMapping( local_ip, internal_port, external_port, protocol, description, duration = duration )
|
||||
try:
|
||||
|
||||
HydrusNATPunch.AddUPnPMapping( local_ip, internal_port, external_port, protocol, description, duration = duration )
|
||||
|
||||
except HydrusExceptions.FirewallException:
|
||||
|
||||
HydrusData.Print( 'The UPnP Daemon tried to add ' + local_ip + ':' + internal_port + '->external:' + external_port + ' but it failed due to router error. Please try it manually to get a full log of what happened.' )
|
||||
|
||||
return
|
||||
|
||||
except:
|
||||
|
||||
raise
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -593,6 +593,7 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
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_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 )
|
||||
|
@ -635,23 +636,26 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
loaded_dictionary = HydrusSerialisable.CreateFromSerialisableTuple( old_serialisable_info )
|
||||
|
||||
mimes = loaded_dictionary[ 'media_view' ].keys()
|
||||
|
||||
for mime in mimes:
|
||||
if 'media_view' in loaded_dictionary:
|
||||
|
||||
if mime in self._dictionary[ 'media_view' ]:
|
||||
mimes = loaded_dictionary[ 'media_view' ].keys()
|
||||
|
||||
for mime in mimes:
|
||||
|
||||
( 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 ]
|
||||
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 ]
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import ClientGUIDialogs
|
|||
import ClientGUIDialogsManage
|
||||
import ClientGUIManagement
|
||||
import ClientGUIPages
|
||||
import ClientGUIPanels
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientDownloading
|
||||
import ClientMedia
|
||||
|
@ -91,8 +91,9 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
self._controller.sub( self, 'NewPageImportBooru', 'new_import_booru' )
|
||||
self._controller.sub( self, 'NewPageImportGallery', 'new_import_gallery' )
|
||||
self._controller.sub( self, 'NewPageImportHDD', 'new_hdd_import' )
|
||||
self._controller.sub( self, 'NewPageImportThreadWatcher', 'new_page_import_thread_watcher' )
|
||||
self._controller.sub( self, 'NewPageImportPageOfImages', 'new_page_import_page_of_images' )
|
||||
self._controller.sub( self, 'NewPageImportThreadWatcher', 'new_page_import_thread_watcher' )
|
||||
self._controller.sub( self, 'NewPageImportURLs', 'new_page_import_urls' )
|
||||
self._controller.sub( self, 'NewPagePetitions', 'new_page_petitions' )
|
||||
self._controller.sub( self, 'NewPageQuery', 'new_page_query' )
|
||||
self._controller.sub( self, 'NewPageThreadDumper', 'new_thread_dumper' )
|
||||
|
@ -124,10 +125,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
self._RefreshStatusBar()
|
||||
|
||||
# as we are in oninit, callafter and calllater( 1ms ) are different
|
||||
# later waits until the mainloop is running, I think.
|
||||
# after seems to execute synchronously
|
||||
|
||||
default_gui_session = HC.options[ 'default_gui_session' ]
|
||||
|
||||
existing_session_names = self._controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION )
|
||||
|
@ -158,11 +155,11 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
if load_a_blank_page:
|
||||
|
||||
wx.CallLater( 1, self._NewPageQuery, CC.LOCAL_FILE_SERVICE_KEY )
|
||||
self._NewPageQuery( CC.LOCAL_FILE_SERVICE_KEY )
|
||||
|
||||
else:
|
||||
|
||||
wx.CallLater( 1, self._LoadGUISession, default_gui_session )
|
||||
self._LoadGUISession( default_gui_session )
|
||||
|
||||
|
||||
wx.CallLater( 5 * 60 * 1000, self.SaveLastSession )
|
||||
|
@ -834,13 +831,16 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
sessions.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'save_gui_session' ), p( 'Save Current' ) )
|
||||
|
||||
if len( gui_session_names ) > 0:
|
||||
if len( gui_session_names ) > 0 and gui_session_names != [ 'last session' ]:
|
||||
|
||||
delete = wx.Menu()
|
||||
|
||||
for name in gui_session_names:
|
||||
|
||||
delete.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'delete_gui_session', name ), name )
|
||||
if name != 'last session':
|
||||
|
||||
delete.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'delete_gui_session', name ), name )
|
||||
|
||||
|
||||
|
||||
sessions.AppendMenu( CC.ID_NULL, p( 'Delete' ), delete )
|
||||
|
@ -895,6 +895,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
download_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'new_import_page_of_images' ), p( '&New Page of Images Download Page' ), p( 'Open a new tab to download files from generic galleries or threads.' ) )
|
||||
download_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'new_import_thread_watcher' ), p( '&New Thread Watcher Page' ), p( 'Open a new tab to watch a thread.' ) )
|
||||
download_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'new_import_urls' ), p( '&New URL Download Page' ), p( 'Open a new tab to download some raw urls.' ) )
|
||||
|
||||
submenu = wx.Menu()
|
||||
|
||||
|
@ -933,8 +934,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
download_popup_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'start_ipfs_download' ), p( '&An IPFS Multihash' ), p( 'Enter an IPFS multihash and attempt to import whatever is returned' ) )
|
||||
|
||||
|
||||
download_popup_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'start_url_download' ), p( '&A Raw URL' ), p( 'Enter a normal URL and attempt to import whatever is returned' ) )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, p( 'New Download Popup' ), download_popup_menu )
|
||||
|
||||
#
|
||||
|
@ -1359,7 +1358,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
with ClientGUITopLevelWindows.DialogManage( self, title, frame_key ) as dlg:
|
||||
|
||||
panel = ClientGUIPanels.ManageOptionsPanel( dlg )
|
||||
panel = ClientGUIScrolledPanels.ManageOptionsPanel( dlg )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
|
@ -1491,6 +1490,13 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
self._NewPage( page_name, management_controller )
|
||||
|
||||
|
||||
def _NewPageImportPageOfImages( self ):
|
||||
|
||||
management_controller = ClientGUIManagement.CreateManagementControllerImportPageOfImages()
|
||||
|
||||
self._NewPage( 'download', management_controller )
|
||||
|
||||
|
||||
def _NewPageImportThreadWatcher( self ):
|
||||
|
||||
management_controller = ClientGUIManagement.CreateManagementControllerImportThreadWatcher()
|
||||
|
@ -1498,11 +1504,11 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
self._NewPage( 'thread watcher', management_controller )
|
||||
|
||||
|
||||
def _NewPageImportPageOfImages( self ):
|
||||
def _NewPageImportURLs( self ):
|
||||
|
||||
management_controller = ClientGUIManagement.CreateManagementControllerImportPageOfImages()
|
||||
management_controller = ClientGUIManagement.CreateManagementControllerImportURLs()
|
||||
|
||||
self._NewPage( 'download', management_controller )
|
||||
self._NewPage( 'url import', management_controller )
|
||||
|
||||
|
||||
def _NewPagePetitions( self, service_key = None ):
|
||||
|
@ -1733,7 +1739,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, self._controller.PrepStringForDisplay( 'Review Services' ), 'review_services' )
|
||||
|
||||
panel = ClientGUIPanels.ReviewServices( frame, self._controller )
|
||||
panel = ClientGUIScrolledPanels.ReviewServices( frame, self._controller )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
@ -1888,27 +1894,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
|
||||
|
||||
def _StartURLDownload( self ):
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, 'Enter URL.' ) as dlg:
|
||||
|
||||
result = dlg.ShowModal()
|
||||
|
||||
if result == wx.ID_OK:
|
||||
|
||||
url = dlg.GetValue()
|
||||
|
||||
url_string = url
|
||||
|
||||
job_key = ClientThreading.JobKey( pausable = True, cancellable = True )
|
||||
|
||||
self._controller.pub( 'message', job_key )
|
||||
|
||||
self._controller.CallToThread( ClientDownloading.THREADDownloadURL, job_key, url, url_string )
|
||||
|
||||
|
||||
|
||||
|
||||
def _StartYoutubeDownload( self ):
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, 'Enter YouTube URL.' ) as dlg:
|
||||
|
@ -2405,8 +2390,9 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
self._NewPageImportGallery( gallery_identifier )
|
||||
|
||||
elif command == 'new_import_thread_watcher': self._NewPageImportThreadWatcher()
|
||||
elif command == 'new_import_page_of_images': self._NewPageImportPageOfImages()
|
||||
elif command == 'new_import_thread_watcher': self._NewPageImportThreadWatcher()
|
||||
elif command == 'new_import_urls': self._NewPageImportURLs()
|
||||
elif command == 'new_page':
|
||||
|
||||
with ClientGUIDialogs.DialogPageChooser( self ) as dlg: dlg.ShowModal()
|
||||
|
@ -2455,7 +2441,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
elif command == 'site': webbrowser.open( 'https://hydrusnetwork.github.io/hydrus/' )
|
||||
elif command == 'start_ipfs_download': self._StartIPFSDownload()
|
||||
elif command == 'start_url_download': self._StartURLDownload()
|
||||
elif command == 'start_youtube_download': self._StartYoutubeDownload()
|
||||
elif command == 'stats': self._Stats( data )
|
||||
elif command == 'synchronised_wait_switch': self._SetSynchronisedWait()
|
||||
|
@ -2638,9 +2623,11 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
self._NewPage( 'import', management_controller )
|
||||
|
||||
|
||||
def NewPageImportPageOfImages( self ): self._NewPageImportPageOfImages()
|
||||
|
||||
def NewPageImportThreadWatcher( self ): self._NewPageImportThreadWatcher()
|
||||
|
||||
def NewPageImportPageOfImages( self ): self._NewPageImportPageOfImages()
|
||||
def NewPageImportURLs( self ): self._NewPageImportURLs()
|
||||
|
||||
def NewPagePetitions( self, service_key ): self._NewPagePetitions( service_key )
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import ClientGUICommon
|
|||
import ClientGUIDialogs
|
||||
import ClientGUIDialogsManage
|
||||
import ClientGUIHoverFrames
|
||||
import ClientGUIPanels
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientMedia
|
||||
import ClientRatings
|
||||
|
@ -1161,7 +1161,7 @@ class Canvas( wx.Window ):
|
|||
|
||||
manage_tags = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIPanels.ManageTagsPanel( manage_tags, self._file_service_key, ( self._current_display_media, ), immediate_commit = True, canvas_key = self._canvas_key )
|
||||
panel = ClientGUIScrolledPanels.ManageTagsPanel( manage_tags, self._file_service_key, ( self._current_display_media, ), immediate_commit = True, canvas_key = self._canvas_key )
|
||||
|
||||
manage_tags.SetPanel( panel )
|
||||
|
||||
|
@ -3589,7 +3589,7 @@ class MediaContainer( wx.Window ):
|
|||
|
||||
if do_embed_button and self._show_action in ( CC.MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED, CC.MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED_PAUSED ):
|
||||
|
||||
self._embed_button = EmbedButton( self, media_initial_size )
|
||||
self._embed_button = EmbedButton( self, self._media, media_initial_size )
|
||||
self._embed_button.Bind( wx.EVT_LEFT_DOWN, self.EventEmbedButton )
|
||||
|
||||
return
|
||||
|
@ -3784,16 +3784,31 @@ class MediaContainer( wx.Window ):
|
|||
|
||||
class EmbedButton( wx.Window ):
|
||||
|
||||
def __init__( self, parent, size ):
|
||||
def __init__( self, parent, media, size ):
|
||||
|
||||
wx.Window.__init__( self, parent, size = size )
|
||||
|
||||
self._media = media
|
||||
|
||||
self._dirty = True
|
||||
|
||||
( x, y ) = size
|
||||
|
||||
self._canvas_bmp = wx.EmptyBitmap( x, y, 24 )
|
||||
|
||||
if self._media.GetLocationsManager().HasLocal() and self._media.GetMime() in HC.MIMES_WITH_THUMBNAILS:
|
||||
|
||||
hash = self._media.GetHash()
|
||||
|
||||
thumbnail_path = HydrusGlobals.client_controller.GetClientFilesManager().GetFullSizeThumbnailPath( hash )
|
||||
|
||||
self._thumbnail_bmp = ClientRendering.GenerateHydrusBitmap( thumbnail_path ).GetWxBitmap()
|
||||
|
||||
else:
|
||||
|
||||
self._thumbnail_bmp = None
|
||||
|
||||
|
||||
self.SetCursor( wx.StockCursor( wx.CURSOR_HAND ) )
|
||||
|
||||
self.Bind( wx.EVT_PAINT, self.EventPaint )
|
||||
|
@ -3805,41 +3820,63 @@ class EmbedButton( wx.Window ):
|
|||
|
||||
( x, y ) = self.GetClientSize()
|
||||
|
||||
background_brush = wx.Brush( wx.Colour( *HC.options[ 'gui_colours' ][ 'media_background' ] ) )
|
||||
|
||||
dc.SetBackground( background_brush )
|
||||
|
||||
dc.Clear()
|
||||
|
||||
center_x = x / 2
|
||||
center_y = y / 2
|
||||
radius = min( center_x, center_y ) - 5
|
||||
radius = min( 50, center_x, center_y ) - 5
|
||||
|
||||
dc.SetPen( wx.TRANSPARENT_PEN )
|
||||
if self._thumbnail_bmp is not None:
|
||||
|
||||
# animations will have the animation bar space underneath in this case, so colour it in
|
||||
dc.SetBackground( wx.Brush( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) )
|
||||
|
||||
dc.Clear()
|
||||
|
||||
( thumb_width, thumb_height ) = self._thumbnail_bmp.GetSize()
|
||||
|
||||
scale = x / float( thumb_width )
|
||||
|
||||
dc.SetUserScale( scale, scale )
|
||||
|
||||
dc.DrawBitmap( self._thumbnail_bmp, 0, 0 )
|
||||
|
||||
dc.SetUserScale( 1.0, 1.0 )
|
||||
|
||||
else:
|
||||
|
||||
dc.SetBackground( wx.Brush( wx.Colour( *HC.options[ 'gui_colours' ][ 'media_background' ] ) ) )
|
||||
|
||||
dc.Clear()
|
||||
|
||||
|
||||
dc.SetBrush( wx.Brush( wx.Colour( 235, 235, 235 ) ) )
|
||||
dc.SetBrush( wx.Brush( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) ) )
|
||||
|
||||
dc.DrawCircle( center_x, center_y, radius )
|
||||
|
||||
dc.SetBrush( background_brush )
|
||||
dc.SetBrush( wx.Brush( wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOW ) ) )
|
||||
|
||||
m = ( 2 ** 0.5 ) / 2 # 45 degree angle
|
||||
# play symbol is a an equilateral triangle
|
||||
|
||||
half_radius = radius / 2
|
||||
triangle_side = radius * 0.8
|
||||
|
||||
angle_half_radius = m * half_radius
|
||||
half_triangle_side = int( triangle_side / 2 )
|
||||
|
||||
cos30 = 0.866
|
||||
|
||||
triangle_width = triangle_side * 0.866
|
||||
|
||||
third_triangle_width = int( triangle_width / 3 )
|
||||
|
||||
points = []
|
||||
|
||||
points.append( ( center_x - angle_half_radius, center_y - angle_half_radius ) )
|
||||
points.append( ( center_x + half_radius, center_y ) )
|
||||
points.append( ( center_x - angle_half_radius, center_y + angle_half_radius ) )
|
||||
points.append( ( center_x - third_triangle_width, center_y - half_triangle_side ) )
|
||||
points.append( ( center_x + third_triangle_width * 2, center_y ) )
|
||||
points.append( ( center_x - third_triangle_width, center_y + half_triangle_side ) )
|
||||
|
||||
dc.DrawPolygon( points )
|
||||
|
||||
#
|
||||
|
||||
dc.SetPen( wx.Pen( wx.Colour( 215, 215, 215 ) ) )
|
||||
dc.SetPen( wx.Pen( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNSHADOW ) ) )
|
||||
|
||||
dc.SetBrush( wx.TRANSPARENT_BRUSH )
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ def WrapInGrid( parent, rows, expand_text = False ):
|
|||
|
||||
text_flags = CC.FLAGS_VCENTER_EXPAND_DEPTH_ONLY # Trying to expand both ways nixes the center. This seems to work right.
|
||||
control_flags = CC.FLAGS_VCENTER
|
||||
sizer_flags = CC.FLAGS_SIZER_VCENTER
|
||||
|
||||
else:
|
||||
|
||||
|
@ -88,12 +89,22 @@ def WrapInGrid( parent, rows, expand_text = False ):
|
|||
|
||||
text_flags = CC.FLAGS_VCENTER
|
||||
control_flags = CC.FLAGS_EXPAND_BOTH_WAYS
|
||||
sizer_flags = CC.FLAGS_EXPAND_SIZER_BOTH_WAYS
|
||||
|
||||
|
||||
for ( text, control ) in rows:
|
||||
|
||||
if isinstance( control, wx.Sizer ):
|
||||
|
||||
cflags = sizer_flags
|
||||
|
||||
else:
|
||||
|
||||
cflags = control_flags
|
||||
|
||||
|
||||
gridbox.AddF( wx.StaticText( parent, label = text ), text_flags )
|
||||
gridbox.AddF( control, control_flags )
|
||||
gridbox.AddF( control, cflags )
|
||||
|
||||
|
||||
return gridbox
|
||||
|
@ -2398,6 +2409,16 @@ class ListBox( wx.ScrolledWindow ):
|
|||
|
||||
def _GetTextColour( self, text ): return ( 0, 111, 250 )
|
||||
|
||||
def _HandleClick( self, event ):
|
||||
|
||||
hit_index = self._GetIndexUnderMouse( event )
|
||||
|
||||
shift = event.ShiftDown()
|
||||
ctrl = event.CmdDown()
|
||||
|
||||
self._Hit( shift, ctrl, hit_index )
|
||||
|
||||
|
||||
def _Hit( self, shift, ctrl, hit_index ):
|
||||
|
||||
if hit_index is not None:
|
||||
|
@ -2690,12 +2711,7 @@ class ListBox( wx.ScrolledWindow ):
|
|||
|
||||
def EventMouseSelect( self, event ):
|
||||
|
||||
hit_index = self._GetIndexUnderMouse( event )
|
||||
|
||||
shift = event.ShiftDown()
|
||||
ctrl = event.CmdDown()
|
||||
|
||||
self._Hit( shift, ctrl, hit_index )
|
||||
self._HandleClick( event )
|
||||
|
||||
event.Skip()
|
||||
|
||||
|
@ -2761,6 +2777,8 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
has_counts = False
|
||||
|
||||
can_spawn_new_windows = True
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
|
||||
ListBox.__init__( self, *args, **kwargs )
|
||||
|
@ -2856,7 +2874,7 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
else:
|
||||
|
||||
text = term
|
||||
text = HydrusData.ToUnicode( term )
|
||||
|
||||
|
||||
if command == 'copy_sub_terms' and ':' in text:
|
||||
|
@ -2920,27 +2938,20 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
def EventMouseMiddleClick( self, event ):
|
||||
|
||||
hit_index = self._GetIndexUnderMouse( event )
|
||||
self._HandleClick( event )
|
||||
|
||||
shift = event.ShiftDown()
|
||||
ctrl = event.CmdDown()
|
||||
|
||||
self._Hit( shift, ctrl, hit_index )
|
||||
|
||||
self._NewSearchPage()
|
||||
if self.can_spawn_new_windows:
|
||||
|
||||
self._NewSearchPage()
|
||||
|
||||
|
||||
|
||||
def EventMouseRightClick( self, event ):
|
||||
|
||||
hit_index = self._GetIndexUnderMouse( event )
|
||||
|
||||
shift = event.ShiftDown()
|
||||
ctrl = event.CmdDown()
|
||||
|
||||
self._Hit( shift, ctrl, hit_index )
|
||||
self._HandleClick( event )
|
||||
|
||||
if len( self._ordered_strings ) > 0:
|
||||
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
if len( self._selected_terms ) > 0:
|
||||
|
@ -2962,7 +2973,7 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
else:
|
||||
|
||||
selection_string = '"' + term + '"'
|
||||
selection_string = '"' + HydrusData.ToUnicode( term ) + '"'
|
||||
|
||||
|
||||
else:
|
||||
|
@ -3005,9 +3016,15 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'new_search_page' ), 'open a new search page for ' + selection_string )
|
||||
if self.can_spawn_new_windows:
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'new_search_page' ), 'open a new search page for ' + selection_string )
|
||||
|
||||
|
||||
menu.AppendSeparator()
|
||||
if menu.GetMenuItemCount() > 0:
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_terms' ), 'copy ' + selection_string )
|
||||
|
||||
|
@ -3037,7 +3054,7 @@ class ListBoxTags( ListBox ):
|
|||
if self.has_counts: menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_all_tags_with_counts' ), 'copy all tags with counts' )
|
||||
|
||||
|
||||
if len( self._selected_terms ) > 0:
|
||||
if self.can_spawn_new_windows and len( self._selected_terms ) > 0:
|
||||
|
||||
term_types = [ type( term ) for term in self._selected_terms ]
|
||||
|
||||
|
@ -3391,6 +3408,8 @@ class ListBoxTagsCensorship( ListBoxTags ):
|
|||
|
||||
class ListBoxTagsColourOptions( ListBoxTags ):
|
||||
|
||||
can_spawn_new_windows = False
|
||||
|
||||
def __init__( self, parent, initial_namespace_colours ):
|
||||
|
||||
ListBoxTags.__init__( self, parent )
|
||||
|
@ -4381,9 +4400,15 @@ class PopupWindow( wx.Window ):
|
|||
self.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
|
||||
|
||||
|
||||
def TryToDismiss( self ): self.GetParent().Dismiss( self )
|
||||
def TryToDismiss( self ):
|
||||
|
||||
self.GetParent().Dismiss( self )
|
||||
|
||||
|
||||
def EventDismiss( self, event ): self.TryToDismiss()
|
||||
def EventDismiss( self, event ):
|
||||
|
||||
self.TryToDismiss()
|
||||
|
||||
|
||||
class PopupDismissAll( PopupWindow ):
|
||||
|
||||
|
@ -4406,11 +4431,20 @@ class PopupDismissAll( PopupWindow ):
|
|||
self.SetSizer( hbox )
|
||||
|
||||
|
||||
def TryToDismiss( self ): pass
|
||||
def TryToDismiss( self ):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def EventButton( self, event ): self.GetParent().DismissAll()
|
||||
def EventButton( self, event ):
|
||||
|
||||
self.GetParent().DismissAll()
|
||||
|
||||
|
||||
def SetNumMessages( self, num_messages_pending ): self._text.SetLabelText( HydrusData.ConvertIntToPrettyString( num_messages_pending ) + ' more messages' )
|
||||
def SetNumMessages( self, num_messages_pending ):
|
||||
|
||||
self._text.SetLabelText( HydrusData.ConvertIntToPrettyString( num_messages_pending ) + ' more messages' )
|
||||
|
||||
|
||||
class PopupMessage( PopupWindow ):
|
||||
|
||||
|
@ -4641,20 +4675,24 @@ class PopupMessage( PopupWindow ):
|
|||
return self._job_key
|
||||
|
||||
|
||||
def TryToDismiss( self ):
|
||||
def IsDeleted( self ):
|
||||
|
||||
if self._job_key.IsPausable() or self._job_key.IsCancellable(): return
|
||||
else: PopupWindow.TryToDismiss( self )
|
||||
return self._job_key.IsDeleted()
|
||||
|
||||
|
||||
def Update( self ):
|
||||
def TryToDismiss( self ):
|
||||
|
||||
if self._job_key.IsDeleted():
|
||||
|
||||
self.TryToDismiss()
|
||||
if self._job_key.IsPausable() or self._job_key.IsCancellable():
|
||||
|
||||
return
|
||||
|
||||
else:
|
||||
|
||||
PopupWindow.TryToDismiss( self )
|
||||
|
||||
|
||||
|
||||
def Update( self ):
|
||||
|
||||
if self._job_key.HasVariable( 'popup_title' ):
|
||||
|
||||
|
@ -4858,6 +4896,8 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
num_messages_displayed = self._message_vbox.GetItemCount()
|
||||
|
||||
self._pending_job_keys = [ job_key for job_key in self._pending_job_keys if not job_key.IsDeleted() ]
|
||||
|
||||
if len( self._pending_job_keys ) > 0 and num_messages_displayed < self._max_messages_to_display:
|
||||
|
||||
job_key = self._pending_job_keys.pop( 0 )
|
||||
|
@ -4901,39 +4941,60 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
try:
|
||||
|
||||
self.Fit()
|
||||
|
||||
parent = self.GetParent()
|
||||
|
||||
( parent_width, parent_height ) = parent.GetClientSize()
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
my_x = ( parent_width - my_width ) - 5
|
||||
my_y = ( parent_height - my_height ) - 15
|
||||
|
||||
self.SetPosition( parent.ClientToScreenXY( my_x, my_y ) )
|
||||
do_restore = False
|
||||
do_show = False
|
||||
|
||||
num_messages_displayed = self._message_vbox.GetItemCount()
|
||||
|
||||
if num_messages_displayed > 0:
|
||||
if self.GetParent().IsIconized():
|
||||
|
||||
current_focus = wx.Window.FindFocus()
|
||||
|
||||
tlp = wx.GetTopLevelParent( current_focus )
|
||||
|
||||
show_happened = self.Show()
|
||||
|
||||
if show_happened and tlp is not None:
|
||||
if not self.IsIconized():
|
||||
|
||||
self.Raise()
|
||||
|
||||
tlp.Raise()
|
||||
self.Iconize()
|
||||
|
||||
|
||||
else:
|
||||
|
||||
self.Hide()
|
||||
if self.IsIconized():
|
||||
|
||||
self.Restore()
|
||||
|
||||
|
||||
|
||||
if num_messages_displayed > 0:
|
||||
|
||||
if not self.IsShown():
|
||||
|
||||
do_show = True
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if self.IsShown():
|
||||
|
||||
self.Hide()
|
||||
|
||||
|
||||
|
||||
if do_show or self.IsShown():
|
||||
|
||||
self.Fit()
|
||||
|
||||
parent = self.GetParent()
|
||||
|
||||
( parent_width, parent_height ) = parent.GetClientSize()
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
my_x = ( parent_width - my_width ) - 5
|
||||
my_y = ( parent_height - my_height ) - 15
|
||||
|
||||
self.SetPosition( parent.ClientToScreenXY( my_x, my_y ) )
|
||||
|
||||
|
||||
if do_show:
|
||||
|
||||
self.Show()
|
||||
|
||||
|
||||
except:
|
||||
|
@ -5005,7 +5066,8 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
self._message_vbox.Detach( window )
|
||||
|
||||
window.Destroy()
|
||||
# OS X segfaults if this is instant
|
||||
wx.CallAfter( window.Destroy )
|
||||
|
||||
self._SizeAndPositionAndShow()
|
||||
|
||||
|
@ -5056,7 +5118,16 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
message_window = sizer_item.GetWindow()
|
||||
|
||||
message_window.Update()
|
||||
if message_window.IsDeleted():
|
||||
|
||||
message_window.TryToDismiss()
|
||||
|
||||
break
|
||||
|
||||
else:
|
||||
|
||||
message_window.Update()
|
||||
|
||||
|
||||
|
||||
self._SizeAndPositionAndShow()
|
||||
|
|
|
@ -2828,8 +2828,9 @@ class DialogPageChooser( Dialog ):
|
|||
|
||||
button.SetLabelText( text )
|
||||
|
||||
elif entry_type == 'page_import_thread_watcher': button.SetLabelText( 'thread watcher' )
|
||||
elif entry_type == 'page_import_page_of_images': button.SetLabelText( 'page of images' )
|
||||
elif entry_type == 'page_import_thread_watcher': button.SetLabelText( 'thread watcher' )
|
||||
elif entry_type == 'page_import_urls': button.SetLabelText( 'raw urls' )
|
||||
|
||||
button.Show()
|
||||
|
||||
|
@ -2867,9 +2868,10 @@ class DialogPageChooser( Dialog ):
|
|||
|
||||
elif menu_keyword == 'download':
|
||||
|
||||
entries.append( ( 'page_import_page_of_images', None ) )
|
||||
entries.append( ( 'page_import_urls', None ) )
|
||||
entries.append( ( 'page_import_thread_watcher', None ) )
|
||||
entries.append( ( 'menu', 'gallery' ) )
|
||||
entries.append( ( 'page_import_page_of_images', None ) )
|
||||
|
||||
elif menu_keyword == 'gallery':
|
||||
|
||||
|
@ -2957,13 +2959,17 @@ class DialogPageChooser( Dialog ):
|
|||
|
||||
HydrusGlobals.client_controller.pub( 'new_import_gallery', site_type )
|
||||
|
||||
elif entry_type == 'page_import_page_of_images':
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'new_page_import_page_of_images' )
|
||||
|
||||
elif entry_type == 'page_import_thread_watcher':
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'new_page_import_thread_watcher' )
|
||||
|
||||
elif entry_type == 'page_import_page_of_images':
|
||||
elif entry_type == 'page_import_urls':
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'new_page_import_page_of_images' )
|
||||
HydrusGlobals.client_controller.pub( 'new_page_import_urls' )
|
||||
|
||||
elif entry_type == 'page_petitions':
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import ClientGUIDialogs
|
|||
import ClientDownloading
|
||||
import ClientGUIOptionsPanels
|
||||
import ClientGUIPredicates
|
||||
import ClientGUIPanels
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientImporting
|
||||
import ClientMedia
|
||||
|
@ -3062,7 +3062,7 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
|
|||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'file import status' ) as dlg:
|
||||
|
||||
panel = ClientGUIPanels.EditSeedCachePanel( dlg, HydrusGlobals.client_controller, dupe_seed_cache )
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( dlg, HydrusGlobals.client_controller, dupe_seed_cache )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
|
@ -5560,7 +5560,7 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'file import status' ) as dlg:
|
||||
|
||||
panel = ClientGUIPanels.EditSeedCachePanel( dlg, HydrusGlobals.client_controller, dupe_seed_cache )
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( dlg, HydrusGlobals.client_controller, dupe_seed_cache )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import ClientGUICollapsible
|
|||
import ClientGUICommon
|
||||
import ClientGUIDialogs
|
||||
import ClientGUIMedia
|
||||
import ClientGUIPanels
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientImporting
|
||||
import ClientMedia
|
||||
|
@ -48,6 +48,7 @@ MANAGEMENT_TYPE_IMPORT_HDD = 3
|
|||
MANAGEMENT_TYPE_IMPORT_THREAD_WATCHER = 4
|
||||
MANAGEMENT_TYPE_PETITIONS = 5
|
||||
MANAGEMENT_TYPE_QUERY = 6
|
||||
MANAGEMENT_TYPE_IMPORT_URLS = 7
|
||||
|
||||
management_panel_types_to_classes = {}
|
||||
|
||||
|
@ -108,6 +109,16 @@ def CreateManagementControllerImportThreadWatcher():
|
|||
|
||||
return management_controller
|
||||
|
||||
def CreateManagementControllerImportURLs():
|
||||
|
||||
management_controller = CreateManagementController( MANAGEMENT_TYPE_IMPORT_URLS )
|
||||
|
||||
urls_import = ClientImporting.URLsImport()
|
||||
|
||||
management_controller.SetVariable( 'urls_import', urls_import )
|
||||
|
||||
return management_controller
|
||||
|
||||
def CreateManagementControllerPetitions( petition_service_key ):
|
||||
|
||||
petition_service = HydrusGlobals.client_controller.GetServicesManager().GetService( petition_service_key )
|
||||
|
@ -1834,7 +1845,7 @@ class ManagementPanelGalleryImport( ManagementPanel ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
@ -1982,7 +1993,7 @@ class ManagementPanelHDDImport( ManagementPanel ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
@ -2191,6 +2202,16 @@ class ManagementPanelPageOfImagesImport( ManagementPanel ):
|
|||
current_action = ''
|
||||
|
||||
|
||||
if self._parser_status.GetLabelText() != parser_status:
|
||||
|
||||
self._parser_status.SetLabelText( parser_status )
|
||||
|
||||
|
||||
if self._current_action.GetLabelText() != current_action:
|
||||
|
||||
self._current_action.SetLabelText( current_action )
|
||||
|
||||
|
||||
if paused:
|
||||
|
||||
if self._pause_button.GetBitmap() != CC.GlobalBMPs.play:
|
||||
|
@ -2206,16 +2227,6 @@ class ManagementPanelPageOfImagesImport( ManagementPanel ):
|
|||
|
||||
|
||||
|
||||
if self._parser_status.GetLabelText() != parser_status:
|
||||
|
||||
self._parser_status.SetLabelText( parser_status )
|
||||
|
||||
|
||||
if self._current_action.GetLabelText() != current_action:
|
||||
|
||||
self._current_action.SetLabelText( current_action )
|
||||
|
||||
|
||||
|
||||
def EventAdvance( self, event ):
|
||||
|
||||
|
@ -2359,7 +2370,7 @@ class ManagementPanelPageOfImagesImport( ManagementPanel ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
@ -3158,7 +3169,7 @@ class ManagementPanelThreadWatcherImport( ManagementPanel ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
@ -3206,3 +3217,252 @@ class ManagementPanelThreadWatcherImport( ManagementPanel ):
|
|||
|
||||
|
||||
management_panel_types_to_classes[ MANAGEMENT_TYPE_IMPORT_THREAD_WATCHER ] = ManagementPanelThreadWatcherImport
|
||||
|
||||
class ManagementPanelURLsImport( ManagementPanel ):
|
||||
|
||||
def __init__( self, parent, page, controller, management_controller ):
|
||||
|
||||
ManagementPanel.__init__( self, parent, page, controller, management_controller )
|
||||
|
||||
self._url_panel = ClientGUICommon.StaticBox( self, 'raw url downloader' )
|
||||
|
||||
self._overall_status = wx.StaticText( self._url_panel )
|
||||
self._current_action = wx.StaticText( self._url_panel )
|
||||
self._file_gauge = ClientGUICommon.Gauge( self._url_panel )
|
||||
self._overall_gauge = ClientGUICommon.Gauge( self._url_panel )
|
||||
|
||||
self._pause_button = wx.BitmapButton( self._url_panel, bitmap = CC.GlobalBMPs.pause )
|
||||
self._pause_button.Bind( wx.EVT_BUTTON, self.EventPause )
|
||||
|
||||
self._waiting_politely_indicator = ClientGUICommon.GetWaitingPolitelyControl( self._url_panel, self._page_key )
|
||||
|
||||
self._seed_cache_button = wx.BitmapButton( self._url_panel, bitmap = CC.GlobalBMPs.seed_cache )
|
||||
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
|
||||
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
|
||||
|
||||
self._url_input = wx.TextCtrl( self._url_panel, style = wx.TE_PROCESS_ENTER )
|
||||
self._url_input.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
|
||||
|
||||
self._url_paste = wx.Button( self._url_panel, label = 'paste urls' )
|
||||
self._url_paste.Bind( wx.EVT_BUTTON, self.EventPaste )
|
||||
|
||||
self._import_file_options = ClientGUICollapsible.CollapsibleOptionsImportFiles( self._url_panel )
|
||||
|
||||
#
|
||||
|
||||
button_sizer = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
button_sizer.AddF( self._waiting_politely_indicator, CC.FLAGS_VCENTER )
|
||||
button_sizer.AddF( self._seed_cache_button, CC.FLAGS_VCENTER )
|
||||
button_sizer.AddF( self._pause_button, CC.FLAGS_VCENTER )
|
||||
|
||||
input_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
input_hbox.AddF( self._url_input, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
input_hbox.AddF( self._url_paste, CC.FLAGS_VCENTER )
|
||||
|
||||
self._url_panel.AddF( self._overall_status, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._url_panel.AddF( self._current_action, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._url_panel.AddF( self._file_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._url_panel.AddF( self._overall_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._url_panel.AddF( button_sizer, CC.FLAGS_BUTTON_SIZER )
|
||||
self._url_panel.AddF( input_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
self._url_panel.AddF( self._import_file_options, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
self._MakeSort( vbox )
|
||||
|
||||
vbox.AddF( self._url_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self._MakeCurrentSelectionTagsBox( vbox )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
#
|
||||
|
||||
self.Bind( wx.EVT_MENU, self.EventMenu )
|
||||
|
||||
self._controller.sub( self, 'UpdateStatus', 'update_status' )
|
||||
|
||||
self._urls_import = self._management_controller.GetVariable( 'urls_import' )
|
||||
|
||||
def file_download_hook( gauge_range, gauge_value ):
|
||||
|
||||
self._file_gauge.SetRange( gauge_range )
|
||||
self._file_gauge.SetValue( gauge_value )
|
||||
|
||||
|
||||
self._urls_import.SetDownloadHook( file_download_hook )
|
||||
|
||||
import_file_options = self._urls_import.GetOptions()
|
||||
|
||||
self._import_file_options.SetOptions( import_file_options )
|
||||
|
||||
self._Update()
|
||||
|
||||
self._urls_import.Start( self._page_key )
|
||||
|
||||
|
||||
def _Update( self ):
|
||||
|
||||
( ( overall_status, ( overall_value, overall_range ) ), paused ) = self._urls_import.GetStatus()
|
||||
|
||||
if self._overall_status.GetLabelText() != overall_status:
|
||||
|
||||
self._overall_status.SetLabelText( overall_status )
|
||||
|
||||
|
||||
self._overall_gauge.SetRange( overall_range )
|
||||
self._overall_gauge.SetValue( overall_value )
|
||||
|
||||
if overall_value < overall_range:
|
||||
|
||||
if paused:
|
||||
|
||||
current_action = 'paused at ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
||||
|
||||
else:
|
||||
|
||||
current_action = 'processing ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
current_action = ''
|
||||
|
||||
|
||||
if self._current_action.GetLabelText() != current_action:
|
||||
|
||||
self._current_action.SetLabelText( current_action )
|
||||
|
||||
|
||||
if paused:
|
||||
|
||||
if self._pause_button.GetBitmap() != CC.GlobalBMPs.play:
|
||||
|
||||
self._pause_button.SetBitmap( CC.GlobalBMPs.play )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if self._pause_button.GetBitmap() != CC.GlobalBMPs.pause:
|
||||
|
||||
self._pause_button.SetBitmap( CC.GlobalBMPs.pause )
|
||||
|
||||
|
||||
|
||||
|
||||
def EventKeyDown( self, event ):
|
||||
|
||||
if event.KeyCode in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
|
||||
|
||||
url = self._url_input.GetValue()
|
||||
|
||||
if url != '':
|
||||
|
||||
self._urls_import.PendURLs( ( url, ) )
|
||||
|
||||
self._url_input.SetValue( '' )
|
||||
|
||||
self._Update()
|
||||
|
||||
|
||||
else:
|
||||
|
||||
event.Skip()
|
||||
|
||||
|
||||
|
||||
def EventMenu( self, event ):
|
||||
|
||||
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
||||
|
||||
if action is not None:
|
||||
|
||||
( command, data ) = action
|
||||
|
||||
if command == 'import_file_options_changed':
|
||||
|
||||
import_file_options = self._import_file_options.GetOptions()
|
||||
|
||||
self._urls_import.SetImportFileOptions( import_file_options )
|
||||
|
||||
else:
|
||||
|
||||
event.Skip()
|
||||
|
||||
|
||||
|
||||
|
||||
def EventPaste( self, event ):
|
||||
|
||||
if wx.TheClipboard.Open():
|
||||
|
||||
data = wx.TextDataObject()
|
||||
|
||||
wx.TheClipboard.GetData( data )
|
||||
|
||||
wx.TheClipboard.Close()
|
||||
|
||||
raw_text = data.GetText()
|
||||
|
||||
try:
|
||||
|
||||
urls = HydrusData.SplitByLinesep( raw_text )
|
||||
|
||||
if len( urls ) > 0:
|
||||
|
||||
self._urls_import.PendURLs( urls )
|
||||
|
||||
|
||||
self._Update()
|
||||
|
||||
except:
|
||||
|
||||
wx.MessageBox( 'I could not understand what was in the clipboard' )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
||||
|
||||
|
||||
|
||||
def EventPause( self, event ):
|
||||
|
||||
self._urls_import.PausePlay()
|
||||
|
||||
self._Update()
|
||||
|
||||
|
||||
def EventSeedCache( self, event ):
|
||||
|
||||
seed_cache = self._urls_import.GetSeedCache()
|
||||
|
||||
title = 'file import status'
|
||||
frame_key = 'file_import_status'
|
||||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
||||
def SetSearchFocus( self, page_key ):
|
||||
|
||||
if page_key == self._page_key: self._url_input.SetFocus()
|
||||
|
||||
|
||||
def UpdateStatus( self, page_key ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self._Update()
|
||||
|
||||
|
||||
|
||||
management_panel_types_to_classes[ MANAGEMENT_TYPE_IMPORT_URLS ] = ManagementPanelURLsImport
|
||||
|
|
|
@ -7,7 +7,7 @@ import ClientGUICommon
|
|||
import ClientGUIDialogs
|
||||
import ClientGUIDialogsManage
|
||||
import ClientGUICanvas
|
||||
import ClientGUIPanels
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientMedia
|
||||
import collections
|
||||
|
@ -799,7 +799,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
with ClientGUITopLevelWindows.DialogManage( self, title, frame_key ) as dlg:
|
||||
|
||||
panel = ClientGUIPanels.ManageTagsPanel( dlg, self._file_service_key, self._selected_media )
|
||||
panel = ClientGUIScrolledPanels.ManageTagsPanel( dlg, self._file_service_key, self._selected_media )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import HydrusConstants as HC
|
|||
import HydrusData
|
||||
import HydrusExceptions
|
||||
import HydrusGlobals
|
||||
import HydrusHTMLParsing
|
||||
import HydrusNATPunch
|
||||
import HydrusPaths
|
||||
import HydrusSerialisable
|
||||
|
@ -134,6 +135,264 @@ class EditFrameLocationPanel( EditPanel ):
|
|||
return ( name, remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen )
|
||||
|
||||
|
||||
class EditHTMLFormulaPanel( EditPanel ):
|
||||
|
||||
def __init__( self, parent, info ):
|
||||
|
||||
EditPanel.__init__( self, parent )
|
||||
|
||||
self._original_info = info
|
||||
|
||||
self._do_testing_automatically = False
|
||||
|
||||
formula_panel = ClientGUICommon.StaticBox( self, 'formula' )
|
||||
|
||||
self._tag_rules = wx.ListBox( formula_panel, style = wx.LB_SINGLE )
|
||||
self._tag_rules.Bind( wx.EVT_LEFT_DCLICK, self.EventEdit )
|
||||
|
||||
self._add_rule = wx.Button( formula_panel, label = 'add' )
|
||||
self._add_rule.Bind( wx.EVT_BUTTON, self.EventAdd )
|
||||
|
||||
self._edit_rule = wx.Button( formula_panel, label = 'edit' )
|
||||
self._edit_rule.Bind( wx.EVT_BUTTON, self.EventEdit )
|
||||
|
||||
self._move_rule_up = wx.Button( formula_panel, label = u'\u2191' )
|
||||
self._move_rule_up.Bind( wx.EVT_BUTTON, self.EventMoveUp )
|
||||
|
||||
self._delete_rule = wx.Button( formula_panel, label = 'X' )
|
||||
self._delete_rule.Bind( wx.EVT_BUTTON, self.EventDelete )
|
||||
|
||||
self._move_rule_down = wx.Button( formula_panel, label = u'\u2193' )
|
||||
self._move_rule_down.Bind( wx.EVT_BUTTON, self.EventMoveDown )
|
||||
|
||||
self._content_rule = wx.TextCtrl( formula_panel )
|
||||
|
||||
testing_panel = ClientGUICommon.StaticBox( self, 'testing' )
|
||||
|
||||
self._test_html = wx.TextCtrl( testing_panel, style = wx.TE_MULTILINE )
|
||||
|
||||
self._fetch_from_url = wx.Button( testing_panel, label = 'fetch html from url' )
|
||||
self._fetch_from_url.Bind( wx.EVT_BUTTON, self.EventFetchFromURL )
|
||||
|
||||
self._run_test = wx.Button( testing_panel, label = 'run test' )
|
||||
self._run_test.Bind( wx.EVT_BUTTON, self.EventRunTest )
|
||||
|
||||
self._results = wx.TextCtrl( testing_panel, style = wx.TE_MULTILINE )
|
||||
|
||||
#
|
||||
|
||||
( tag_rules, content_rule ) = self._original_info.ToTuple()
|
||||
|
||||
for rule in tag_rules:
|
||||
|
||||
pretty_rule = HydrusHTMLParsing.RenderTagRule( rule )
|
||||
|
||||
self._tag_rules.Append( pretty_rule, rule )
|
||||
|
||||
|
||||
self._content_rule.SetValue( content_rule )
|
||||
|
||||
self._test_html.SetValue( 'Enter html here to test it against the above formula.' )
|
||||
self._results.SetValue( 'Successfully parsed results will be printed here.' )
|
||||
|
||||
#
|
||||
|
||||
udd_button_vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
udd_button_vbox.AddF( self._move_rule_up, CC.FLAGS_VCENTER )
|
||||
udd_button_vbox.AddF( self._delete_rule, CC.FLAGS_VCENTER )
|
||||
udd_button_vbox.AddF( self._move_rule_down, CC.FLAGS_VCENTER )
|
||||
|
||||
tag_rules_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
tag_rules_hbox.AddF( self._tag_rules, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
tag_rules_hbox.AddF( udd_button_vbox, CC.FLAGS_VCENTER )
|
||||
|
||||
ae_button_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
ae_button_hbox.AddF( self._add_rule, CC.FLAGS_VCENTER )
|
||||
ae_button_hbox.AddF( self._edit_rule, CC.FLAGS_VCENTER )
|
||||
|
||||
formula_panel.AddF( tag_rules_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
formula_panel.AddF( ae_button_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
formula_panel.AddF( ClientGUICommon.WrapInText( self._content_rule, formula_panel, 'attribute: ' ), CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
testing_panel.AddF( self._test_html, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
testing_panel.AddF( self._fetch_from_url, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
testing_panel.AddF( self._run_test, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
testing_panel.AddF( self._results, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
message = 'The html will be searched recursively by each rule in turn and then the attribute of the final tags will be returned.'
|
||||
message += os.linesep * 2
|
||||
message += 'So, to find the \'src\' of the first <img> tag beneath all <span> tags with the class \'content\', use:'
|
||||
message += os.linesep * 2
|
||||
message += 'all span tags with class=content'
|
||||
message += '1st img tag'
|
||||
message += 'attribute: src'
|
||||
message += os.linesep * 2
|
||||
message += 'Leave the attribute blank to represent the string of the tag (i.e. <p>This part</p>).'
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = message ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( formula_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( testing_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def _RunTest( self ):
|
||||
|
||||
formula = self.GetValue()
|
||||
|
||||
html = self._test_html.GetValue()
|
||||
|
||||
try:
|
||||
|
||||
results = formula.Parse( html )
|
||||
|
||||
# do the begin/end to better display '' results and any other whitespace weirdness
|
||||
results = [ '*** RESULTS BEGIN ***' ] + results + [ '*** RESULTS END ***' ]
|
||||
|
||||
results_text = os.linesep.join( results )
|
||||
|
||||
self._results.SetValue( results_text )
|
||||
|
||||
self._do_testing_automatically = True
|
||||
|
||||
except Exception as e:
|
||||
|
||||
message = 'Could not parse! Full error written to log!'
|
||||
message += os.linesep * 2
|
||||
message += HydrusData.ToUnicode( e )
|
||||
|
||||
wx.MessageBox( message )
|
||||
|
||||
self._do_testing_automatically = False
|
||||
|
||||
|
||||
|
||||
def EventAdd( self, event ):
|
||||
|
||||
# spawn dialog, add it and run test
|
||||
|
||||
if self._do_testing_automatically:
|
||||
|
||||
self._RunTest()
|
||||
|
||||
|
||||
|
||||
def EventDelete( self, event ):
|
||||
|
||||
selection = self._tag_rules.GetSelection()
|
||||
|
||||
if selection != wx.NOT_FOUND:
|
||||
|
||||
if self._tag_rules.GetCount() == 1:
|
||||
|
||||
wx.MessageBox( 'A parsing formula needs at least one tag rule!' )
|
||||
|
||||
else:
|
||||
|
||||
self._tag_rules.Delete( selection )
|
||||
|
||||
if self._do_testing_automatically:
|
||||
|
||||
self._RunTest()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def EventEdit( self, event ):
|
||||
|
||||
selection = self._tag_rules.GetSelection()
|
||||
|
||||
if selection != wx.NOT_FOUND:
|
||||
|
||||
( name, attrs, index ) = self._tag_rules.GetClientData( selection )
|
||||
|
||||
# spawn dialog, then if ok, set it and run test
|
||||
|
||||
if self._do_testing_automatically:
|
||||
|
||||
self._RunTest()
|
||||
|
||||
|
||||
|
||||
def EventFetchFromURL( self, event ):
|
||||
|
||||
# ask user for url with textdlg
|
||||
# get it with requests
|
||||
# handle errors with a messagebox
|
||||
# try to parse it with bs4 to check it is good html and then splat it to the textctrl, otherwise just messagebox the error
|
||||
|
||||
if self._do_testing_automatically:
|
||||
|
||||
self._RunTest()
|
||||
|
||||
|
||||
|
||||
def EventMoveDown( self, event ):
|
||||
|
||||
selection = self._tag_rules.GetSelection()
|
||||
|
||||
if selection != wx.NOT_FOUND and selection + 1 < self._tag_rules.GetCount():
|
||||
|
||||
pretty_rule = self._tag_rules.GetString( selection )
|
||||
rule = self._tag_rules.GetClientData( selection )
|
||||
|
||||
self._tag_rules.Delete( selection )
|
||||
|
||||
self._tag_rules.Insert( selection + 1, pretty_rule, rule )
|
||||
|
||||
if self._do_testing_automatically:
|
||||
|
||||
self._RunTest()
|
||||
|
||||
|
||||
|
||||
|
||||
def EventMoveUp( self, event ):
|
||||
|
||||
selection = self._tag_rules.GetSelection()
|
||||
|
||||
if selection != wx.NOT_FOUND and selection > 0:
|
||||
|
||||
pretty_rule = self._tag_rules.GetString( selection )
|
||||
rule = self._tag_rules.GetClientData( selection )
|
||||
|
||||
self._tag_rules.Delete( selection )
|
||||
|
||||
self._tag_rules.Insert( selection - 1, pretty_rule, rule )
|
||||
|
||||
if self._do_testing_automatically:
|
||||
|
||||
self._RunTest()
|
||||
|
||||
|
||||
|
||||
|
||||
def EventRunTest( self, event ):
|
||||
|
||||
self._RunTest()
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
tags_rules = [ self._tag_rules.GetClientData( i ) for i in range( self._tag_rules.GetCount() ) ]
|
||||
content_rule = self._content_rule.GetValue()
|
||||
|
||||
if content_rule == '':
|
||||
|
||||
content_rule = None
|
||||
|
||||
|
||||
formula = HydrusHTMLParsing.ParseFormula( tags_rules, content_rule )
|
||||
|
||||
return formula
|
||||
|
||||
|
||||
class EditMediaViewOptionsPanel( EditPanel ):
|
||||
|
||||
def __init__( self, parent, info ):
|
||||
|
@ -998,35 +1257,25 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
#
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'Run maintenance jobs when the client is idle and the system is not otherwise busy: ', self._idle_normal ) )
|
||||
rows.append( ( 'Assume the client is idle if no general browsing activity has occured in the past: ', self._idle_period ) )
|
||||
rows.append( ( 'Assume the client is idle if the mouse has not been moved in the past: ', self._idle_mouse_period ) )
|
||||
rows.append( ( 'Assume the system is busy if any CPU core has recent average usage above: ', self._idle_cpu_max ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self._idle_panel, label = 'Run maintenance jobs when the client is idle and the system is not otherwise busy?: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._idle_normal, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self._idle_panel, label = 'Assume the client is idle if no general browsing activity has occured in the past: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._idle_period, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self._idle_panel, label = 'Assume the client is idle if the mouse has not been moved in the past: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._idle_mouse_period, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self._idle_panel, label = 'Assume the system is busy if any CPU core has recent average usage above: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._idle_cpu_max, CC.FLAGS_VCENTER )
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._idle_panel, rows )
|
||||
|
||||
self._idle_panel.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'Run jobs on shutdown: ', self._idle_shutdown ) )
|
||||
rows.append( ( 'Max number of minutes to run shutdown jobs: ', self._idle_shutdown_max_minutes ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self._shutdown_panel, label = 'Run jobs on shutdown?: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._idle_shutdown, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self._shutdown_panel, label = 'Max number of minutes to run shutdown jobs: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._idle_shutdown_max_minutes, CC.FLAGS_VCENTER )
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._shutdown_panel, rows )
|
||||
|
||||
self._shutdown_panel.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
@ -1050,23 +1299,21 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
#
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'Number of days to wait between vacuums: ', self._maintenance_vacuum_period ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self._maintenance_panel, label = 'Number of days to wait between vacuums: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._maintenance_vacuum_period, CC.FLAGS_VCENTER )
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._maintenance_panel, rows )
|
||||
|
||||
self._maintenance_panel.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'Delay repository update processing by (s): ', self._processing_phase ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self._processing_panel, label = 'Delay repository update processing by (s): ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._processing_phase, CC.FLAGS_VCENTER )
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._processing_panel, rows )
|
||||
|
||||
self._processing_panel.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
|
@ -1388,30 +1635,17 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'Default export directory: ', self._export_location ) )
|
||||
rows.append( ( 'When deleting files or folders, send them to the OS\'s recycle bin: ', self._delete_to_recycle_bin ) )
|
||||
rows.append( ( 'By default, do not reimport files that have been previously deleted: ', self._exclude_deleted_files ) )
|
||||
rows.append( ( 'Remove files from view when they are filtered: ', self._remove_filtered_files ) )
|
||||
rows.append( ( 'Remove files from view when they are sent to the trash: ', self._remove_trashed_files ) )
|
||||
rows.append( ( 'Number of hours a file can be in the trash before being deleted: ', self._trash_max_age ) )
|
||||
rows.append( ( 'Maximum size of trash (MB): ', self._trash_max_size ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Default export directory: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._export_location, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'When deleting files or folders, send them to the OS\'s recycle bin: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._delete_to_recycle_bin, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'By default, do not reimport files that have been previously deleted: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._exclude_deleted_files, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Remove files from view when they are filtered: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._remove_filtered_files, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Remove files from view when they are sent to the trash: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._remove_trashed_files, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Number of hours a file can be in the trash before being deleted: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._trash_max_age, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Maximum size of trash (MB): ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._trash_max_size, CC.FLAGS_VCENTER )
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
text = 'If you set the default export directory blank, the client will use \'hydrus_export\' under the current user\'s home directory.'
|
||||
|
||||
|
@ -1508,36 +1742,19 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
#
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'Default session on startup: ', self._default_gui_session ) )
|
||||
rows.append( ( 'Confirm client exit: ', self._confirm_client_exit ) )
|
||||
rows.append( ( 'Confirm sending files to trash: ', self._confirm_trash ) )
|
||||
rows.append( ( 'Confirm sending more than one file to archive or inbox: ', self._confirm_archive ) )
|
||||
rows.append( ( 'Always embed autocomplete dropdown results window: ', self._always_embed_autocompletes ) )
|
||||
rows.append( ( 'Capitalise gui: ', self._gui_capitalisation ) )
|
||||
rows.append( ( 'Hide the preview window: ', self._hide_preview ) )
|
||||
rows.append( ( 'Show \'title\' banner on thumbnails: ', self._show_thumbnail_title_banner ) )
|
||||
rows.append( ( 'Show volume/chapter/page number on thumbnails: ', self._show_thumbnail_page ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Default session on startup:' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._default_gui_session, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Confirm client exit:' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._confirm_client_exit, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Confirm sending files to trash:' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._confirm_trash, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Confirm sending more than one file to archive or inbox:' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._confirm_archive, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Always embed autocomplete dropdown results window:' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._always_embed_autocompletes, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Capitalise gui: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._gui_capitalisation, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Hide the preview window: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._hide_preview, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Show \'title\' banner on thumbnails: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._show_thumbnail_title_banner, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Show volume/chapter/page number on thumbnails: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._show_thumbnail_page, CC.FLAGS_VCENTER )
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
text = 'Here you can override the current and default values for many frame and dialog sizing and positioning variables.'
|
||||
text += os.linesep
|
||||
|
@ -1651,7 +1868,7 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
self._media_zooms.SetValue( ','.join( ( str( media_zoom ) for media_zoom in media_zooms ) ) )
|
||||
|
||||
mimes_in_correct_order = ( HC.IMAGE_JPEG, HC.IMAGE_PNG, HC.IMAGE_GIF, HC.APPLICATION_FLASH, HC.APPLICATION_PDF, HC.VIDEO_FLV, HC.VIDEO_MP4, HC.VIDEO_MKV, HC.VIDEO_MPEG, HC.VIDEO_WEBM, HC.VIDEO_WMV, HC.AUDIO_MP3, HC.AUDIO_OGG, HC.AUDIO_FLAC, HC.AUDIO_WMA )
|
||||
mimes_in_correct_order = ( HC.IMAGE_JPEG, HC.IMAGE_PNG, HC.IMAGE_GIF, HC.APPLICATION_FLASH, HC.APPLICATION_PDF, HC.VIDEO_FLV, HC.VIDEO_MOV, HC.VIDEO_MP4, HC.VIDEO_MKV, HC.VIDEO_MPEG, HC.VIDEO_WEBM, HC.VIDEO_WMV, HC.AUDIO_MP3, HC.AUDIO_OGG, HC.AUDIO_FLAC, HC.AUDIO_WMA )
|
||||
|
||||
for mime in mimes_in_correct_order:
|
||||
|
||||
|
@ -1669,18 +1886,13 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'Start animations this % in: ', self._animation_start_position ) )
|
||||
rows.append( ( 'Disable OpenCV for gifs: ', self._disable_cv_for_gifs ) )
|
||||
rows.append( ( 'Media zooms: ', self._media_zooms ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Start animations this % in: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._animation_start_position, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Disable OpenCV for gifs: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._disable_cv_for_gifs, CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Media zooms: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._media_zooms, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
@ -1999,16 +2211,13 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
#
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'Default sort: ', self._default_sort ) )
|
||||
rows.append( ( 'Secondary sort (when primary gives two equal values): ', self._sort_fallback ) )
|
||||
rows.append( ( 'Default collect: ', self._default_collect ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'default sort: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._default_sort, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
gridbox.AddF( wx.StaticText( self, label = 'secondary sort (when primary gives two equal values): ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._sort_fallback, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
gridbox.AddF( wx.StaticText( self, label = 'default collect: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._default_collect, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
|
@ -2106,52 +2315,70 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
self._new_options = new_options
|
||||
|
||||
self._disk_cache_init_period = ClientGUICommon.NoneableSpinCtrl( self, 'max disk cache init period', none_phrase = 'do not run', min = 1, max = 120 )
|
||||
disk_panel = ClientGUICommon.StaticBox( self, 'disk cache' )
|
||||
|
||||
self._disk_cache_init_period = ClientGUICommon.NoneableSpinCtrl( disk_panel, 'max disk cache init period', none_phrase = 'do not run', min = 1, max = 120 )
|
||||
self._disk_cache_init_period.SetToolTipString( 'When the client boots, it can speed up operation by reading the front of the database into memory. This sets the max number of seconds it can spend doing that.' )
|
||||
|
||||
self._disk_cache_maintenance_mb = ClientGUICommon.NoneableSpinCtrl( self, 'disk cache maintenance (MB)', none_phrase = 'do not keep db cached', min = 32, max = 65536 )
|
||||
self._disk_cache_maintenance_mb = ClientGUICommon.NoneableSpinCtrl( disk_panel, 'disk cache maintenance (MB)', none_phrase = 'do not keep db cached', min = 32, max = 65536 )
|
||||
self._disk_cache_maintenance_mb.SetToolTipString( 'The client can regularly check the front of its database is cached in memory. This represents how many megabytes it will ensure are cached.' )
|
||||
|
||||
self._thumbnail_width = wx.SpinCtrl( self, min = 20, max = 200 )
|
||||
#
|
||||
|
||||
media_panel = ClientGUICommon.StaticBox( self, 'thumbnail size and media cache' )
|
||||
|
||||
self._thumbnail_width = wx.SpinCtrl( media_panel, min = 20, max = 200 )
|
||||
self._thumbnail_width.Bind( wx.EVT_SPINCTRL, self.EventThumbnailsUpdate )
|
||||
|
||||
self._thumbnail_height = wx.SpinCtrl( self, min = 20, max = 200 )
|
||||
self._thumbnail_height = wx.SpinCtrl( media_panel, min = 20, max = 200 )
|
||||
self._thumbnail_height.Bind( wx.EVT_SPINCTRL, self.EventThumbnailsUpdate )
|
||||
|
||||
self._thumbnail_cache_size = wx.SpinCtrl( self, min = 5, max = 3000 )
|
||||
self._thumbnail_cache_size = wx.SpinCtrl( media_panel, min = 5, max = 3000 )
|
||||
self._thumbnail_cache_size.Bind( wx.EVT_SPINCTRL, self.EventThumbnailsUpdate )
|
||||
|
||||
self._estimated_number_thumbnails = wx.StaticText( self, label = '' )
|
||||
self._estimated_number_thumbnails = wx.StaticText( media_panel, label = '' )
|
||||
|
||||
self._fullscreen_cache_size = wx.SpinCtrl( self, min = 25, max = 3000 )
|
||||
self._fullscreen_cache_size = wx.SpinCtrl( media_panel, min = 25, max = 3000 )
|
||||
self._fullscreen_cache_size.Bind( wx.EVT_SPINCTRL, self.EventFullscreensUpdate )
|
||||
|
||||
self._estimated_number_fullscreens = wx.StaticText( self, label = '' )
|
||||
self._estimated_number_fullscreens = wx.StaticText( media_panel, label = '' )
|
||||
|
||||
self._video_buffer_size_mb = wx.SpinCtrl( self, min = 48, max = 16 * 1024 )
|
||||
#
|
||||
|
||||
buffer_panel = ClientGUICommon.StaticBox( self, 'video buffer' )
|
||||
|
||||
self._video_buffer_size_mb = wx.SpinCtrl( buffer_panel, min = 48, max = 16 * 1024 )
|
||||
self._video_buffer_size_mb.Bind( wx.EVT_SPINCTRL, self.EventVideoBufferUpdate )
|
||||
|
||||
self._estimated_number_video_frames = wx.StaticText( self, label = '' )
|
||||
self._estimated_number_video_frames = wx.StaticText( buffer_panel, label = '' )
|
||||
|
||||
self._forced_search_limit = ClientGUICommon.NoneableSpinCtrl( self, '', min = 1, max = 100000 )
|
||||
#
|
||||
|
||||
self._num_autocomplete_chars = wx.SpinCtrl( self, min = 1, max = 100 )
|
||||
ac_panel = ClientGUICommon.StaticBox( self, 'tag autocomplete' )
|
||||
|
||||
self._num_autocomplete_chars = wx.SpinCtrl( ac_panel, min = 1, max = 100 )
|
||||
self._num_autocomplete_chars.SetToolTipString( 'how many characters you enter before the gui fetches autocomplete results from the db. (otherwise, it will only fetch exact matches)' + os.linesep + 'increase this if you find autocomplete results are slow' )
|
||||
|
||||
self._fetch_ac_results_automatically = wx.CheckBox( self )
|
||||
self._fetch_ac_results_automatically = wx.CheckBox( ac_panel )
|
||||
self._fetch_ac_results_automatically.Bind( wx.EVT_CHECKBOX, self.EventFetchAuto )
|
||||
|
||||
self._autocomplete_long_wait = wx.SpinCtrl( self, min = 0, max = 10000 )
|
||||
self._autocomplete_long_wait = wx.SpinCtrl( ac_panel, min = 0, max = 10000 )
|
||||
self._autocomplete_long_wait.SetToolTipString( 'how long the gui will typically wait, after you enter a character, before it queries the db with what you have entered so far' )
|
||||
|
||||
self._autocomplete_short_wait_chars = wx.SpinCtrl( self, min = 1, max = 100 )
|
||||
self._autocomplete_short_wait_chars = wx.SpinCtrl( ac_panel, min = 1, max = 100 )
|
||||
self._autocomplete_short_wait_chars.SetToolTipString( 'how many characters you enter before the gui starts waiting the short time before querying the db' )
|
||||
|
||||
self._autocomplete_short_wait = wx.SpinCtrl( self, min = 0, max = 10000 )
|
||||
self._autocomplete_short_wait = wx.SpinCtrl( ac_panel, min = 0, max = 10000 )
|
||||
self._autocomplete_short_wait.SetToolTipString( 'how long the gui will typically wait, after you enter a lot of characters, before it queries the db with what you have entered so far' )
|
||||
|
||||
#
|
||||
|
||||
misc_panel = ClientGUICommon.StaticBox( self, 'misc' )
|
||||
|
||||
self._forced_search_limit = ClientGUICommon.NoneableSpinCtrl( misc_panel, '', min = 1, max = 100000 )
|
||||
|
||||
#
|
||||
|
||||
self._disk_cache_init_period.SetValue( self._new_options.GetNoneableInteger( 'disk_cache_init_period' ) )
|
||||
self._disk_cache_maintenance_mb.SetValue( self._new_options.GetNoneableInteger( 'disk_cache_maintenance_mb' ) )
|
||||
|
||||
|
@ -2167,8 +2394,6 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
self._video_buffer_size_mb.SetValue( self._new_options.GetInteger( 'video_buffer_size_mb' ) )
|
||||
|
||||
self._forced_search_limit.SetValue( self._new_options.GetNoneableInteger( 'forced_search_limit' ) )
|
||||
|
||||
self._num_autocomplete_chars.SetValue( HC.options[ 'num_autocomplete_chars' ] )
|
||||
|
||||
self._fetch_ac_results_automatically.SetValue( HC.options[ 'fetch_ac_results_automatically' ] )
|
||||
|
@ -2181,6 +2406,17 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
self._autocomplete_short_wait.SetValue( short_wait )
|
||||
|
||||
self._forced_search_limit.SetValue( self._new_options.GetNoneableInteger( 'forced_search_limit' ) )
|
||||
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
disk_panel.AddF( self._disk_cache_init_period, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
disk_panel.AddF( self._disk_cache_maintenance_mb, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
vbox.AddF( disk_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
thumbnails_sizer = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
@ -2198,34 +2434,20 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
video_buffer_sizer.AddF( self._video_buffer_size_mb, CC.FLAGS_VCENTER )
|
||||
video_buffer_sizer.AddF( self._estimated_number_video_frames, CC.FLAGS_VCENTER )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
rows = []
|
||||
|
||||
vbox.AddF( self._disk_cache_init_period, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._disk_cache_maintenance_mb, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
rows.append( ( 'Thumbnail width: ', self._thumbnail_width ) )
|
||||
rows.append( ( 'Thumbnail height: ', self._thumbnail_height ) )
|
||||
rows.append( ( 'MB memory reserved for thumbnail cache: ', thumbnails_sizer ) )
|
||||
rows.append( ( 'MB memory reserved for media viewer cache: ', fullscreens_sizer ) )
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
gridbox = ClientGUICommon.WrapInGrid( media_panel, rows )
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
media_panel.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Thumbnail width: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._thumbnail_width, CC.FLAGS_VCENTER )
|
||||
vbox.AddF( media_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Thumbnail height: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._thumbnail_height, CC.FLAGS_VCENTER )
|
||||
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'MB memory reserved for thumbnail cache: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( thumbnails_sizer, CC.FLAGS_NONE )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'MB memory reserved for media viewer cache: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( fullscreens_sizer, CC.FLAGS_NONE )
|
||||
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
#
|
||||
|
||||
text = 'Hydrus video rendering is CPU intensive.'
|
||||
text += os.linesep
|
||||
|
@ -2233,50 +2455,51 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
text += os.linesep
|
||||
text += 'If the video buffer can hold an entire video, it only needs to be rendered once and will loop smoothly.'
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = text ), CC.FLAGS_VCENTER )
|
||||
buffer_panel.AddF( wx.StaticText( buffer_panel, label = text ), CC.FLAGS_VCENTER )
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'MB memory for video buffer: ', video_buffer_sizer ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'MB memory for video buffer: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( video_buffer_sizer, CC.FLAGS_NONE )
|
||||
gridbox = ClientGUICommon.WrapInGrid( buffer_panel, rows )
|
||||
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
buffer_panel.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
vbox.AddF( buffer_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Forced system:limit for all searches: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._forced_search_limit, CC.FLAGS_NONE )
|
||||
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
#
|
||||
|
||||
text = 'If you disable automatic autocomplete results fetching, use Ctrl+Space to fetch results manually.'
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = text ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
ac_panel.AddF( wx.StaticText( ac_panel, label = text ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'Automatically fetch autocomplete results after a short delay: ', self._fetch_ac_results_automatically ) )
|
||||
rows.append( ( 'Autocomplete long wait character threshold: ', self._num_autocomplete_chars ) )
|
||||
rows.append( ( 'Autocomplete long wait (ms): ', self._autocomplete_long_wait ) )
|
||||
rows.append( ( 'Autocomplete short wait character threshold: ', self._autocomplete_short_wait_chars ) )
|
||||
rows.append( ( 'Autocomplete short wait (ms): ', self._autocomplete_short_wait ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Automatically fetch autocomplete results after a short delay: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._fetch_ac_results_automatically, CC.FLAGS_VCENTER )
|
||||
gridbox = ClientGUICommon.WrapInGrid( ac_panel, rows )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete long wait character threshold: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._num_autocomplete_chars, CC.FLAGS_VCENTER )
|
||||
ac_panel.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete long wait (ms): ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._autocomplete_long_wait, CC.FLAGS_VCENTER )
|
||||
vbox.AddF( ac_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete short wait character threshold: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._autocomplete_short_wait_chars, CC.FLAGS_VCENTER )
|
||||
#
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete short wait (ms): ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._autocomplete_short_wait, CC.FLAGS_VCENTER )
|
||||
rows = []
|
||||
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
rows.append( ( 'Forced system:limit for all searches: ', self._forced_search_limit ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( misc_panel, rows )
|
||||
|
||||
misc_panel.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
vbox.AddF( misc_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
@ -2367,7 +2590,9 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
self._new_options = new_options
|
||||
|
||||
self._default_tag_sort = wx.Choice( self )
|
||||
general_panel = ClientGUICommon.StaticBox( self, 'general tag options' )
|
||||
|
||||
self._default_tag_sort = wx.Choice( general_panel )
|
||||
|
||||
self._default_tag_sort.Append( 'lexicographic (a-z)', CC.SORT_BY_LEXICOGRAPHIC_ASC )
|
||||
self._default_tag_sort.Append( 'lexicographic (z-a)', CC.SORT_BY_LEXICOGRAPHIC_DESC )
|
||||
|
@ -2376,11 +2601,11 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
self._default_tag_sort.Append( 'incidence (desc)', CC.SORT_BY_INCIDENCE_DESC )
|
||||
self._default_tag_sort.Append( 'incidence (asc)', CC.SORT_BY_INCIDENCE_ASC )
|
||||
|
||||
self._default_tag_repository = ClientGUICommon.BetterChoice( self )
|
||||
self._default_tag_repository = ClientGUICommon.BetterChoice( general_panel )
|
||||
|
||||
self._show_all_tags_in_autocomplete = wx.CheckBox( self )
|
||||
self._show_all_tags_in_autocomplete = wx.CheckBox( general_panel )
|
||||
|
||||
self._apply_all_parents_to_all_services = wx.CheckBox( self )
|
||||
self._apply_all_parents_to_all_services = wx.CheckBox( general_panel )
|
||||
|
||||
suggested_tags_panel = ClientGUICommon.StaticBox( self, 'suggested tags' )
|
||||
|
||||
|
@ -2462,59 +2687,50 @@ class ManageOptionsPanel( ManagePanel ):
|
|||
|
||||
#
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
rows = []
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Default tag service in manage tag dialogs:' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._default_tag_repository, CC.FLAGS_VCENTER )
|
||||
rows.append( ( 'Default tag service in manage tag dialogs: ', self._default_tag_repository ) )
|
||||
rows.append( ( 'Default tag sort: ', self._default_tag_sort ) )
|
||||
rows.append( ( 'By default, search non-local tags in write-autocomplete: ', self._show_all_tags_in_autocomplete ) )
|
||||
rows.append( ( 'Suggest all parents for all services: ', self._apply_all_parents_to_all_services ) )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Default tag sort:' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._default_tag_sort, CC.FLAGS_VCENTER )
|
||||
gridbox = ClientGUICommon.WrapInGrid( general_panel, rows )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'By default, search non-local tags in write-autocomplete: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._show_all_tags_in_autocomplete, CC.FLAGS_VCENTER )
|
||||
general_panel.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Apply all parents to all services: ' ), CC.FLAGS_VCENTER )
|
||||
gridbox.AddF( self._apply_all_parents_to_all_services, CC.FLAGS_VCENTER )
|
||||
vbox.AddF( general_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
suggested_tags_favourites_panel.AddF( self._suggested_favourites_services, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
suggested_tags_favourites_panel.AddF( self._suggested_favourites, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
suggested_tags_favourites_panel.AddF( self._suggested_favourites_input, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
related_gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
rows = []
|
||||
|
||||
related_gridbox.AddGrowableCol( 1, 1 )
|
||||
rows.append( ( 'Show related tags on single-file manage tags windows: ', self._show_related_tags ) )
|
||||
rows.append( ( 'Width of related tags list: ', self._related_tags_width ) )
|
||||
rows.append( ( 'Initial search duration (ms): ', self._related_tags_search_1_duration_ms ) )
|
||||
rows.append( ( 'Medium search duration (ms): ', self._related_tags_search_2_duration_ms ) )
|
||||
rows.append( ( 'Thorough search duration (ms): ', self._related_tags_search_3_duration_ms ) )
|
||||
|
||||
related_gridbox.AddF( wx.StaticText( suggested_tags_related_panel, label = 'show related tags on single-file manage tags windows' ), CC.FLAGS_VCENTER )
|
||||
related_gridbox.AddF( self._show_related_tags, CC.FLAGS_VCENTER )
|
||||
|
||||
related_gridbox.AddF( wx.StaticText( suggested_tags_related_panel, label = 'width of related tags list' ), CC.FLAGS_VCENTER )
|
||||
related_gridbox.AddF( self._related_tags_width, CC.FLAGS_VCENTER )
|
||||
|
||||
related_gridbox.AddF( wx.StaticText( suggested_tags_related_panel, label = 'initial search duration (ms)' ), CC.FLAGS_VCENTER )
|
||||
related_gridbox.AddF( self._related_tags_search_1_duration_ms, CC.FLAGS_VCENTER )
|
||||
|
||||
related_gridbox.AddF( wx.StaticText( suggested_tags_related_panel, label = 'medium search duration (ms)' ), CC.FLAGS_VCENTER )
|
||||
related_gridbox.AddF( self._related_tags_search_2_duration_ms, CC.FLAGS_VCENTER )
|
||||
|
||||
related_gridbox.AddF( wx.StaticText( suggested_tags_related_panel, label = 'thorough search duration (ms)' ), CC.FLAGS_VCENTER )
|
||||
related_gridbox.AddF( self._related_tags_search_3_duration_ms, CC.FLAGS_VCENTER )
|
||||
related_gridbox = ClientGUICommon.WrapInGrid( suggested_tags_related_panel, rows )
|
||||
|
||||
suggested_tags_related_panel.AddF( related_gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
suggested_tags_recent_panel.AddF( self._num_recent_tags, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
suggested_tags_panel.AddF( self._suggested_tags_width, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
suggested_tags_panel.AddF( suggested_tags_favourites_panel, CC.FLAGS_EXPAND_SIZER_DEPTH_ONLY )
|
||||
suggested_tags_panel.AddF( suggested_tags_favourites_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
suggested_tags_panel.AddF( suggested_tags_related_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
suggested_tags_panel.AddF( suggested_tags_recent_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( suggested_tags_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
#
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
#
|
|
@ -197,7 +197,6 @@ def SetTLWSizeAndPosition( tlw, frame_key ):
|
|||
wx.CallAfter( tlw.ShowFullScreen, True, wx.FULLSCREEN_ALL )
|
||||
|
||||
|
||||
|
||||
class NewDialog( wx.Dialog ):
|
||||
|
||||
def __init__( self, parent, title ):
|
||||
|
|
|
@ -2494,6 +2494,17 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
self._WorkOnFiles( job_key )
|
||||
|
||||
except HydrusExceptions.NetworkException as e:
|
||||
|
||||
HydrusData.Print( 'The subscription ' + self._name + ' encountered an exception when trying to sync:' )
|
||||
HydrusData.PrintException( e )
|
||||
|
||||
job_key.SetVariable( 'popup_text_1', 'Encountered a network error, will retry again later' )
|
||||
|
||||
self._last_error = HydrusData.GetNow()
|
||||
|
||||
time.sleep( 5 )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.ShowText( 'The subscription ' + self._name + ' encountered an exception when trying to sync:' )
|
||||
|
@ -2987,3 +2998,243 @@ class ThreadWatcherImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_THREAD_WATCHER_IMPORT ] = ThreadWatcherImport
|
||||
|
||||
class URLsImport( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_URLS_IMPORT
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self ):
|
||||
|
||||
HydrusSerialisable.SerialisableBase.__init__( self )
|
||||
|
||||
import_file_options = ClientDefaults.GetDefaultImportFileOptions()
|
||||
|
||||
self._urls_cache = SeedCache()
|
||||
self._import_file_options = import_file_options
|
||||
self._paused = False
|
||||
|
||||
self._seed_cache_status = ( 'initialising', ( 0, 1 ) )
|
||||
self._file_download_hook = None
|
||||
|
||||
self._lock = threading.Lock()
|
||||
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
||||
serialisable_url_cache = self._urls_cache.GetSerialisableTuple()
|
||||
serialisable_file_options = self._import_file_options.GetSerialisableTuple()
|
||||
|
||||
return ( serialisable_url_cache, serialisable_file_options, self._paused )
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
( serialisable_url_cache, serialisable_file_options, self._paused ) = serialisable_info
|
||||
|
||||
self._urls_cache = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_url_cache )
|
||||
self._import_file_options = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_file_options )
|
||||
|
||||
|
||||
def _RegenerateSeedCacheStatus( self, page_key ):
|
||||
|
||||
new_seed_cache_status = self._urls_cache.GetStatus()
|
||||
|
||||
if self._seed_cache_status != new_seed_cache_status:
|
||||
|
||||
self._seed_cache_status = new_seed_cache_status
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'update_status', page_key )
|
||||
|
||||
|
||||
|
||||
def _WorkOnFiles( self, page_key ):
|
||||
|
||||
do_wait = True
|
||||
|
||||
file_url = self._urls_cache.GetNextSeed( CC.STATUS_UNKNOWN )
|
||||
|
||||
if file_url is None:
|
||||
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
|
||||
( status, hash ) = HydrusGlobals.client_controller.Read( 'url_status', file_url )
|
||||
|
||||
if status == CC.STATUS_DELETED:
|
||||
|
||||
if not self._import_file_options.GetExcludeDeleted():
|
||||
|
||||
status = CC.STATUS_NEW
|
||||
|
||||
|
||||
|
||||
if status == CC.STATUS_NEW:
|
||||
|
||||
( os_file_handle, temp_path ) = HydrusPaths.GetTempPath()
|
||||
|
||||
try:
|
||||
|
||||
report_hooks = []
|
||||
|
||||
with self._lock:
|
||||
|
||||
if self._file_download_hook is not None:
|
||||
|
||||
report_hooks.append( self._file_download_hook )
|
||||
|
||||
|
||||
|
||||
HydrusGlobals.client_controller.DoHTTP( HC.GET, file_url, report_hooks = report_hooks, temp_path = temp_path )
|
||||
|
||||
client_files_manager = HydrusGlobals.client_controller.GetClientFilesManager()
|
||||
|
||||
( status, hash ) = client_files_manager.ImportFile( temp_path, import_file_options = self._import_file_options, url = file_url )
|
||||
|
||||
finally:
|
||||
|
||||
HydrusPaths.CleanUpTempPath( os_file_handle, temp_path )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
do_wait = False
|
||||
|
||||
|
||||
self._urls_cache.UpdateSeedStatus( file_url, status )
|
||||
|
||||
if status in ( CC.STATUS_SUCCESSFUL, CC.STATUS_REDUNDANT ):
|
||||
|
||||
( media_result, ) = HydrusGlobals.client_controller.Read( 'media_results', ( hash, ) )
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'add_media_results', page_key, ( media_result, ) )
|
||||
|
||||
|
||||
except HydrusExceptions.MimeException as e:
|
||||
|
||||
status = CC.STATUS_UNINTERESTING_MIME
|
||||
|
||||
self._urls_cache.UpdateSeedStatus( file_url, status )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
status = CC.STATUS_FAILED
|
||||
|
||||
self._urls_cache.UpdateSeedStatus( file_url, status, exception = e )
|
||||
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._RegenerateSeedCacheStatus( page_key )
|
||||
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'update_status', page_key )
|
||||
|
||||
if do_wait:
|
||||
|
||||
ClientData.WaitPolitely( page_key )
|
||||
|
||||
|
||||
|
||||
def _THREADWork( self, page_key ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._RegenerateSeedCacheStatus( page_key )
|
||||
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'update_status', page_key )
|
||||
|
||||
while not ( HydrusGlobals.view_shutdown or HydrusGlobals.client_controller.PageDeleted( page_key ) ):
|
||||
|
||||
if self._paused or HydrusGlobals.client_controller.PageHidden( page_key ):
|
||||
|
||||
time.sleep( 0.1 )
|
||||
|
||||
else:
|
||||
|
||||
try:
|
||||
|
||||
self._WorkOnFiles( page_key )
|
||||
|
||||
time.sleep( 0.1 )
|
||||
|
||||
HydrusGlobals.client_controller.WaitUntilPubSubsEmpty()
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.ShowException( e )
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def GetSeedCache( self ):
|
||||
|
||||
return self._urls_cache
|
||||
|
||||
|
||||
def GetOptions( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._import_file_options
|
||||
|
||||
|
||||
|
||||
def GetStatus( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return ( self._seed_cache_status, self._paused )
|
||||
|
||||
|
||||
|
||||
def PausePlay( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._paused = not self._paused
|
||||
|
||||
|
||||
|
||||
def PendURLs( self, urls ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
for url in urls:
|
||||
|
||||
if not self._urls_cache.HasSeed( url ):
|
||||
|
||||
self._urls_cache.AddSeed( url )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def SetDownloadHook( self, hook ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._file_download_hook = hook
|
||||
|
||||
|
||||
|
||||
def SetImportFileOptions( self, import_file_options ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._import_file_options = import_file_options
|
||||
|
||||
|
||||
|
||||
def Start( self, page_key ):
|
||||
|
||||
threading.Thread( target = self._THREADWork, args = ( page_key, ) ).start()
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_URLS_IMPORT ] = URLsImport
|
||||
|
|
|
@ -427,6 +427,29 @@ class HTTPConnection( object ):
|
|||
|
||||
|
||||
|
||||
def _ReadResponse( self, response, report_hooks, temp_path = None ):
|
||||
|
||||
try:
|
||||
|
||||
if response.status == 200 and temp_path is not None:
|
||||
|
||||
size_of_response = self._WriteResponseToPath( response, temp_path, report_hooks )
|
||||
|
||||
parsed_response = 'response written to temporary file'
|
||||
|
||||
else:
|
||||
|
||||
( parsed_response, size_of_response ) = self._ParseResponse( response, report_hooks )
|
||||
|
||||
|
||||
except socket.timeout as e:
|
||||
|
||||
raise HydrusExceptions.NetworkException( 'Connection timed out during response read.' )
|
||||
|
||||
|
||||
return ( parsed_response, size_of_response )
|
||||
|
||||
|
||||
def _ParseCookies( self, raw_cookies_string ):
|
||||
|
||||
cookies = {}
|
||||
|
@ -455,7 +478,7 @@ class HTTPConnection( object ):
|
|||
|
||||
|
||||
def _ParseResponse( self, response, report_hooks ):
|
||||
|
||||
|
||||
server_header = response.getheader( 'Server' )
|
||||
|
||||
if server_header is not None and 'hydrus' in server_header:
|
||||
|
@ -636,16 +659,7 @@ class HTTPConnection( object ):
|
|||
|
||||
response = self._GetResponse( method_string, path_and_query, request_headers, body )
|
||||
|
||||
if response.status == 200 and temp_path is not None:
|
||||
|
||||
size_of_response = self._WriteResponseToPath( response, temp_path, report_hooks )
|
||||
|
||||
parsed_response = 'response written to temporary file'
|
||||
|
||||
else:
|
||||
|
||||
( parsed_response, size_of_response ) = self._ParseResponse( response, report_hooks )
|
||||
|
||||
( parsed_response, size_of_response ) = self._ReadResponse( response, report_hooks, temp_path )
|
||||
|
||||
response_headers = { k : v for ( k, v ) in response.getheaders() if k != 'set-cookie' }
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 17
|
||||
SOFTWARE_VERSION = 221
|
||||
SOFTWARE_VERSION = 222
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
@ -275,19 +275,20 @@ APPLICATION_JSON = 22
|
|||
VIDEO_APNG = 23
|
||||
UNDETERMINED_PNG = 24
|
||||
VIDEO_MPEG = 25
|
||||
VIDEO_MOV = 26
|
||||
APPLICATION_OCTET_STREAM = 100
|
||||
APPLICATION_UNKNOWN = 101
|
||||
|
||||
ALLOWED_MIMES = ( IMAGE_JPEG, IMAGE_PNG, IMAGE_GIF, IMAGE_BMP, APPLICATION_FLASH, VIDEO_FLV, VIDEO_MP4, VIDEO_MKV, VIDEO_WEBM, VIDEO_MPEG, APPLICATION_PDF, AUDIO_MP3, AUDIO_OGG, AUDIO_FLAC, AUDIO_WMA, VIDEO_WMV )
|
||||
SEARCHABLE_MIMES = ( IMAGE_JPEG, IMAGE_PNG, IMAGE_GIF, APPLICATION_FLASH, VIDEO_FLV, VIDEO_MP4, VIDEO_MKV, VIDEO_WEBM, VIDEO_MPEG, APPLICATION_PDF, AUDIO_MP3, AUDIO_OGG, AUDIO_FLAC, AUDIO_WMA, VIDEO_WMV )
|
||||
ALLOWED_MIMES = ( IMAGE_JPEG, IMAGE_PNG, IMAGE_GIF, IMAGE_BMP, APPLICATION_FLASH, VIDEO_FLV, VIDEO_MOV, VIDEO_MP4, VIDEO_MKV, VIDEO_WEBM, VIDEO_MPEG, APPLICATION_PDF, AUDIO_MP3, AUDIO_OGG, AUDIO_FLAC, AUDIO_WMA, VIDEO_WMV )
|
||||
SEARCHABLE_MIMES = ( IMAGE_JPEG, IMAGE_PNG, IMAGE_GIF, APPLICATION_FLASH, VIDEO_FLV, VIDEO_MOV, VIDEO_MP4, VIDEO_MKV, VIDEO_WEBM, VIDEO_MPEG, APPLICATION_PDF, AUDIO_MP3, AUDIO_OGG, AUDIO_FLAC, AUDIO_WMA, VIDEO_WMV )
|
||||
|
||||
IMAGES = ( IMAGE_JPEG, IMAGE_PNG, IMAGE_GIF, IMAGE_BMP )
|
||||
|
||||
AUDIO = ( AUDIO_MP3, AUDIO_OGG, AUDIO_FLAC, AUDIO_WMA )
|
||||
|
||||
VIDEO = ( VIDEO_FLV, VIDEO_MP4, VIDEO_WMV, VIDEO_MKV, VIDEO_WEBM, VIDEO_MPEG )
|
||||
VIDEO = ( VIDEO_FLV, VIDEO_MOV, VIDEO_MP4, VIDEO_WMV, VIDEO_MKV, VIDEO_WEBM, VIDEO_MPEG )
|
||||
|
||||
NATIVE_VIDEO = ( VIDEO_FLV, VIDEO_MP4, VIDEO_WMV, VIDEO_MKV, VIDEO_WEBM, VIDEO_MPEG )
|
||||
NATIVE_VIDEO = ( VIDEO_FLV, VIDEO_MOV, VIDEO_MP4, VIDEO_WMV, VIDEO_MKV, VIDEO_WEBM, VIDEO_MPEG )
|
||||
|
||||
APPLICATIONS = ( APPLICATION_FLASH, APPLICATION_PDF, APPLICATION_ZIP )
|
||||
|
||||
|
@ -295,7 +296,7 @@ NOISY_MIMES = tuple( [ APPLICATION_FLASH ] + list( AUDIO ) + list( VIDEO ) )
|
|||
|
||||
ARCHIVES = ( APPLICATION_ZIP, APPLICATION_HYDRUS_ENCRYPTED_ZIP )
|
||||
|
||||
MIMES_WITH_THUMBNAILS = ( APPLICATION_FLASH, IMAGE_JPEG, IMAGE_PNG, IMAGE_GIF, IMAGE_BMP, VIDEO_FLV, VIDEO_MP4, VIDEO_WMV, VIDEO_MKV, VIDEO_WEBM, VIDEO_MPEG )
|
||||
MIMES_WITH_THUMBNAILS = ( APPLICATION_FLASH, IMAGE_JPEG, IMAGE_PNG, IMAGE_GIF, IMAGE_BMP, VIDEO_FLV, VIDEO_MOV, VIDEO_MP4, VIDEO_WMV, VIDEO_MKV, VIDEO_WEBM, VIDEO_MPEG )
|
||||
|
||||
# mp3 header is complicated
|
||||
|
||||
|
@ -327,6 +328,7 @@ mime_enum_lookup[ 'audio/x-ms-wma' ] = AUDIO_WMA
|
|||
mime_enum_lookup[ 'text/html' ] = TEXT_HTML
|
||||
mime_enum_lookup[ 'video/png' ] = VIDEO_APNG
|
||||
mime_enum_lookup[ 'video/x-flv' ] = VIDEO_FLV
|
||||
mime_enum_lookup[ 'video/quicktime' ] = VIDEO_MOV
|
||||
mime_enum_lookup[ 'video/mp4' ] = VIDEO_MP4
|
||||
mime_enum_lookup[ 'video/mpeg' ] = VIDEO_MPEG
|
||||
mime_enum_lookup[ 'video/x-ms-wmv' ] = VIDEO_WMV
|
||||
|
@ -360,6 +362,7 @@ mime_string_lookup[ AUDIO ] = 'audio'
|
|||
mime_string_lookup[ TEXT_HTML ] = 'text/html'
|
||||
mime_string_lookup[ VIDEO_APNG ] = 'video/png'
|
||||
mime_string_lookup[ VIDEO_FLV ] = 'video/x-flv'
|
||||
mime_string_lookup[ VIDEO_MOV ] = 'video/quicktime'
|
||||
mime_string_lookup[ VIDEO_MP4 ] = 'video/mp4'
|
||||
mime_string_lookup[ VIDEO_MPEG ] = 'video/mpeg'
|
||||
mime_string_lookup[ VIDEO_WMV ] = 'video/x-ms-wmv'
|
||||
|
@ -391,6 +394,7 @@ mime_ext_lookup[ AUDIO_WMA ] = '.wma'
|
|||
mime_ext_lookup[ TEXT_HTML ] = '.html'
|
||||
mime_ext_lookup[ VIDEO_APNG ] = '.png'
|
||||
mime_ext_lookup[ VIDEO_FLV ] = '.flv'
|
||||
mime_ext_lookup[ VIDEO_MOV ] = '.mov'
|
||||
mime_ext_lookup[ VIDEO_MP4 ] = '.mp4'
|
||||
mime_ext_lookup[ VIDEO_MPEG ] = '.mpeg'
|
||||
mime_ext_lookup[ VIDEO_WMV ] = '.wmv'
|
||||
|
|
|
@ -174,6 +174,11 @@ def ConvertIntToBytes( size ):
|
|||
|
||||
return '%.0f' % size + suffixes[ suffix_index ] + 'B'
|
||||
|
||||
def ConvertIntToFirst( n ):
|
||||
|
||||
# straight from stack, wew
|
||||
return "%d%s" % (n,"tsnrhtdd"[(n/10%10!=1)*(n%10<4)*n%10::4])
|
||||
|
||||
def ConvertIntToPixels( i ):
|
||||
|
||||
if i == 1: return 'pixels'
|
||||
|
|
|
@ -37,6 +37,7 @@ header_and_mime = [
|
|||
( 4, 'ftypmp4', HC.VIDEO_MP4 ),
|
||||
( 4, 'ftypisom', HC.VIDEO_MP4 ),
|
||||
( 4, 'ftypM4V', HC.VIDEO_MP4 ),
|
||||
( 4, 'ftypqt', HC.VIDEO_MOV ),
|
||||
( 0, 'fLaC', HC.AUDIO_FLAC ),
|
||||
( 0, '\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C', HC.UNDETERMINED_WM )
|
||||
]
|
||||
|
@ -168,7 +169,7 @@ def GetFileInfo( path ):
|
|||
|
||||
( ( width, height ), duration, num_frames ) = HydrusFlashHandling.GetFlashProperties( path )
|
||||
|
||||
elif mime in ( HC.VIDEO_FLV, HC.VIDEO_WMV, HC.VIDEO_MP4, HC.VIDEO_MKV, HC.VIDEO_WEBM, HC.VIDEO_MPEG ):
|
||||
elif mime in ( HC.VIDEO_FLV, HC.VIDEO_WMV, HC.VIDEO_MOV, HC.VIDEO_MP4, HC.VIDEO_MKV, HC.VIDEO_WEBM, HC.VIDEO_MPEG ):
|
||||
|
||||
( ( width, height ), duration, num_frames ) = HydrusVideoHandling.GetFFMPEGVideoProperties( path )
|
||||
|
||||
|
|
|
@ -1,16 +1,45 @@
|
|||
import bs4
|
||||
import HydrusData
|
||||
import HydrusSerialisable
|
||||
|
||||
def RenderTagRule( ( name, attrs, index ) ):
|
||||
|
||||
if index is None:
|
||||
|
||||
result = 'all ' + name + ' tags'
|
||||
|
||||
else:
|
||||
|
||||
result = HydrusData.ConvertIntToFirst( index + 1 ) + name + ' tag'
|
||||
|
||||
|
||||
if len( attrs ) > 0:
|
||||
|
||||
result += ' with ' + ' and '.join( [ key + ' = ' + value for ( key, value ) in attrs.items() ] )
|
||||
|
||||
|
||||
return result
|
||||
|
||||
class ParseFormula( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_HTML_PARSE_FORMULA
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self ):
|
||||
def __init__( self, tag_rules = None, content_rule = None ):
|
||||
|
||||
self._tag_rules = []
|
||||
if tag_rules is None:
|
||||
|
||||
tag_rules = [ ( 'a', {}, None ) ]
|
||||
|
||||
|
||||
self._content_rule = None
|
||||
if content_rule is None:
|
||||
|
||||
content_rule = 'src'
|
||||
|
||||
|
||||
self._tag_rules = tag_rules
|
||||
|
||||
self._content_rule = content_rule
|
||||
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
@ -58,35 +87,6 @@ class ParseFormula( HydrusSerialisable.SerialisableBase ):
|
|||
return results
|
||||
|
||||
|
||||
def IsValid( self ):
|
||||
|
||||
return len( self._tag_rules ) > 0 and self._content_rule is not None
|
||||
|
||||
|
||||
def PopTagsRule( self ):
|
||||
|
||||
self._tag_rules.pop()
|
||||
|
||||
|
||||
def PushTagsRule( self, name = None, attrs = {}, index = None ):
|
||||
|
||||
self._tag_rules.append( ( name, attrs, index ) )
|
||||
|
||||
|
||||
def Duplicate( self ):
|
||||
|
||||
new_formula = ParseFormula()
|
||||
|
||||
for ( name, attrs, index ) in self._tag_rules:
|
||||
|
||||
new_formula.PushTagsRule( name, dict( attrs ), index )
|
||||
|
||||
|
||||
new_formula.SetContentRule( self._content_rule )
|
||||
|
||||
return new_formula
|
||||
|
||||
|
||||
def Parse( self, html ):
|
||||
|
||||
root = bs4.BeautifulSoup( html, 'lxml' )
|
||||
|
@ -110,9 +110,9 @@ class ParseFormula( HydrusSerialisable.SerialisableBase ):
|
|||
return contents
|
||||
|
||||
|
||||
def SetContentRule( self, attr = None ):
|
||||
def ToTuple( self ):
|
||||
|
||||
self._content_rule = attr
|
||||
return ( self._tag_rules, self._content_rule )
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_HTML_PARSE_FORMULA ] = ParseFormula
|
|
@ -81,12 +81,19 @@ def AddUPnPMapping( internal_client, internal_port, external_port, protocol, des
|
|||
|
||||
if 'x.x.x.x:' + str( external_port ) + ' TCP is redirected to internal ' + internal_client + ':' + str( internal_port ) in output:
|
||||
|
||||
raise Exception( 'The UPnP mapping of ' + internal_client + ':' + internal_port + '->external:' + external_port + ' already exists as a port forward. If this UPnP mapping is automatic, please disable it.' )
|
||||
raise HydrusExceptions.FirewallException( 'The UPnP mapping of ' + internal_client + ':' + internal_port + '->external:' + external_port + ' already exists as a port forward. If this UPnP mapping is automatic, please disable it.' )
|
||||
|
||||
|
||||
if output is not None and 'failed with code' in output:
|
||||
|
||||
raise Exception( 'Problem while trying to add UPnP mapping:' + os.linesep * 2 + HydrusData.ToUnicode( output ) )
|
||||
if 'UnknownError' in output:
|
||||
|
||||
raise HydrusExceptions.FirewallException( 'Problem while trying to add UPnP mapping:' + os.linesep * 2 + HydrusData.ToUnicode( output ) )
|
||||
|
||||
else:
|
||||
|
||||
raise Exception( 'Problem while trying to add UPnP mapping:' + os.linesep * 2 + HydrusData.ToUnicode( output ) )
|
||||
|
||||
|
||||
|
||||
if error is not None and len( error ) > 0:
|
||||
|
|
|
@ -29,6 +29,7 @@ SERIALISABLE_TYPE_SERVER_TO_CLIENT_PETITION = 24
|
|||
SERIALISABLE_TYPE_ACCOUNT_IDENTIFIER = 25
|
||||
SERIALISABLE_TYPE_LIST = 26
|
||||
SERIALISABLE_TYPE_HTML_PARSE_FORMULA = 27
|
||||
SERIALISABLE_TYPE_URLS_IMPORT = 28
|
||||
|
||||
SERIALISABLE_TYPES_TO_OBJECT_TYPES = {}
|
||||
|
||||
|
|
Loading…
Reference in New Issue