Version 144

This commit is contained in:
Hydrus 2015-01-14 16:27:55 -06:00
parent d05ba33fb9
commit d13afc0285
16 changed files with 1777 additions and 2140 deletions

View File

@ -8,6 +8,30 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 144</h3></li>
<ul>
<li>files named 'Thumbs.db' will now be skipped in the import files dialog</li>
<li>fixed wildcard searches, which last week's predicate rewrite broke</li>
<li>fixed a typo that was showing namespace predicates as exclusive (-series:*) when they were actually inclusive (series:*) and vice versa</li>
<li>added wildcard namespace searching for database autocomplete queries</li>
<li>fixed database wildcard autocomplete searching when wildcard match is not the first word in a tag</li>
<li>fixed database file searching when wildcard match is not the first word in a tag</li>
<li>added a comprehensive suite of predicate-unicode conversion tests</li>
<li>cleaned and improved some of the downloader code</li>
<li>added five second per-gallery-page delay to subscription daemon</li>
<li>added three second per-file-delay for regular gallery downloads and subscriptions, just to be polite to those web services</li>
<li>added three second per-file-delay to the thread watcher for the same reason</li>
<li>added a 'check now' button to the thread watcher</li>
<li>fixed a problem that was sometimes causing subscriptions, when paused, to continually restart</li>
<li>removed unnamespaced tag parsing from deviant art</li>
<li>fixed creator parsing for deviant art, which was formerly cutting off the first character</li>
<li>patched an account sync problem in the manage tags dialog</li>
<li>in add tags by path dialog, tags are now sorted before being added to the file list</li>
<li>in add tags by path dialog, the regex sections now generate tags for every match in the string, not just the first</li>
<li>stopped collapsible panels resizing dialogs to minsize on collapse or expand</li>
<li>added shortcut for 'open externally', default Ctrl+E</li>
<li>moved 8chan to new 8ch.net domain (old domain still works)</li>
</ul>
<li><h3>version 143</h3></li>
<ul>
<li>when making a READ autocomplete tag query, instances of tags that only have a count in a single namespaced domain will no longer accumulate helper results in the non-namespaced domain i.e. no more 'blah (1)' 'title:blah (1)' dupes</li>

View File

@ -262,6 +262,7 @@ shortcuts[ wx.ACCEL_CTRL ][ ord( 'M' ) ] = 'set_media_focus'
shortcuts[ wx.ACCEL_CTRL ][ ord( 'I' ) ] = 'synchronised_wait_switch'
shortcuts[ wx.ACCEL_CTRL ][ ord( 'Z' ) ] = 'undo'
shortcuts[ wx.ACCEL_CTRL ][ ord( 'Y' ) ] = 'redo'
shortcuts[ wx.ACCEL_CTRL ][ ord( 'E' ) ] = 'open_externally'
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_UP ] = 'previous'
shortcuts[ wx.ACCEL_NORMAL ][ wx.WXK_LEFT ] = 'previous'

View File

@ -1849,7 +1849,14 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
def GetPossibleTagIds():
def GetPossibleWildcardNamespaceIds( wildcard_namespace ):
wildcard_namespace = wildcard_namespace.replace( '*', '%' )
return [ namespace_id for ( namespace_id, ) in self._c.execute( 'SELECT namespace_id FROM namespaces WHERE namespace LIKE ?;', ( wildcard_namespace, ) ) ]
def GetPossibleTagIds( h_c_t ):
# the issue is that the tokenizer for fts4 doesn't like weird characters
# a search for '[s' actually only does 's'
@ -1857,10 +1864,10 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
# note that queries with '*' are passed to LIKE, because MATCH only supports appended wildcards 'gun*', and not complex stuff like '*gun*'
if half_complete_tag_can_be_matched: return [ tag_id for ( tag_id, ) in self._c.execute( 'SELECT docid FROM tags_fts4 WHERE tag MATCH ?;', ( '"' + half_complete_tag + '*"', ) ) ]
if half_complete_tag_can_be_matched: return [ tag_id for ( tag_id, ) in self._c.execute( 'SELECT docid FROM tags_fts4 WHERE tag MATCH ?;', ( '"' + h_c_t + '*"', ) ) ]
else:
possible_tag_ids_half_complete_tag = half_complete_tag
possible_tag_ids_half_complete_tag = h_c_t
if '*' in possible_tag_ids_half_complete_tag:
@ -1868,7 +1875,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
else: possible_tag_ids_half_complete_tag += '%'
return [ tag_id for ( tag_id, ) in self._c.execute( 'SELECT tag_id FROM tags WHERE tag LIKE ? OR tag LIKE ?;', ( possible_tag_ids_half_complete_tag, ' ' + possible_tag_ids_half_complete_tag ) ) ]
return [ tag_id for ( tag_id, ) in self._c.execute( 'SELECT tag_id FROM tags WHERE tag LIKE ? OR tag LIKE ?;', ( possible_tag_ids_half_complete_tag, '% ' + possible_tag_ids_half_complete_tag ) ) ]
@ -1881,22 +1888,34 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
if half_complete_tag == '': return CC.AutocompleteMatchesCounted( {} )
else:
result = self._c.execute( 'SELECT namespace_id FROM namespaces WHERE namespace = ?;', ( namespace, ) ).fetchone()
if result is None: return CC.AutocompleteMatchesCounted( {} )
if '*' in namespace:
possible_namespace_ids = GetPossibleWildcardNamespaceIds( namespace )
predicates_phrase_1 = 'namespace_id IN ' + HC.SplayListForDB( possible_namespace_ids )
else:
( namespace_id, ) = result
possible_tag_ids = GetPossibleTagIds()
result = self._c.execute( 'SELECT namespace_id FROM namespaces WHERE namespace = ?;', ( namespace, ) ).fetchone()
predicates_phrase = 'namespace_id = ' + HC.u( namespace_id ) + ' AND tag_id IN ' + HC.SplayListForDB( possible_tag_ids )
if result is None: return CC.AutocompleteMatchesCounted( {} )
else:
( namespace_id, ) = result
predicates_phrase_1 = 'namespace_id = ' + HC.u( namespace_id )
possible_tag_ids = GetPossibleTagIds( half_complete_tag )
predicates_phrase = predicates_phrase_1 + ' AND tag_id IN ' + HC.SplayListForDB( possible_tag_ids )
else:
possible_tag_ids = GetPossibleTagIds()
possible_tag_ids = GetPossibleTagIds( half_complete_tag )
predicates_phrase = 'tag_id IN ' + HC.SplayListForDB( possible_tag_ids )
@ -2632,7 +2651,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
w = w.replace( '*', '%' )
return { tag_id for ( tag_id, ) in self._c.execute( 'SELECT tag_id FROM tags WHERE tag LIKE ?;', ( w, ) ) }
return { tag_id for ( tag_id, ) in self._c.execute( 'SELECT tag_id FROM tags WHERE tag LIKE ? or tag LIKE ?;', ( w, '% ' + w ) ) }
else:
@ -5326,25 +5345,6 @@ class DB( ServiceDB ):
def _UpdateDB( self, version ):
if version == 94:
# I changed a variable name in account, so old yaml dumps need to be refreshed
unknown_account = HC.GetUnknownAccount()
self._c.execute( 'UPDATE accounts SET account = ?;', ( unknown_account, ) )
for ( name, info ) in self._c.execute( 'SELECT name, info FROM gui_sessions;' ).fetchall():
for ( page_name, c_text, args, kwargs ) in info:
if 'do_query' in kwargs: del kwargs[ 'do_query' ]
self._c.execute( 'UPDATE gui_sessions SET info = ? WHERE name = ?;', ( info, name ) )
if version == 95:
self._c.execute( 'COMMIT' )
@ -5964,6 +5964,15 @@ class DB( ServiceDB ):
self._c.execute( 'REPLACE INTO yaml_dumps VALUES ( ?, ?, ? );', ( YAML_DUMP_ID_REMOTE_BOORU, 'sankaku chan', CC.DEFAULT_BOORUS[ 'sankaku chan' ] ) )
if version == 143:
( HC.options, ) = self._c.execute( 'SELECT options FROM options;' ).fetchone()
HC.options[ 'shortcuts' ][ wx.ACCEL_CTRL ][ ord( 'E' ) ] = 'open_externally'
self._c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
HC.is_db_updated = True
@ -7223,7 +7232,7 @@ def DAEMONSynchroniseRepositories():
def DAEMONSynchroniseSubscriptions():
HC.repos_changed = False
HC.subs_changed = False
if not HC.options[ 'pause_subs_sync' ]:
@ -7373,6 +7382,8 @@ def DAEMONSynchroniseSubscriptions():
job_key.SetVariable( 'popup_message_text_1', 'found ' + HC.ConvertIntToPrettyString( len( all_url_args ) ) + ' new files' )
time.sleep( 5 )
for downloader in downloaders_to_remove: downloaders.remove( downloader )
@ -7503,6 +7514,8 @@ def DAEMONSynchroniseSubscriptions():
HC.app.WaitUntilGoodTimeToUseGUIThread()
time.sleep( 3 )
job_key.DeleteVariable( 'popup_message_gauge_1' )

View File

@ -705,12 +705,15 @@ class Canvas( object ):
def _OpenExternally( self ):
hash = self._current_display_media.GetHash()
mime = self._current_display_media.GetMime()
path = CC.GetFilePath( hash, mime )
HC.LaunchFile( path )
if self._current_display_media is not None:
hash = self._current_display_media.GetHash()
mime = self._current_display_media.GetMime()
path = CC.GetFilePath( hash, mime )
HC.LaunchFile( path )
def _PrefetchImages( self ): pass

View File

@ -3364,18 +3364,20 @@ class CollapsiblePanel( wx.Panel ):
parent_of_container = self.GetParent().GetParent()
old_height = self.GetMinHeight()
parent_of_container.Layout()
new_height = self.GetMinHeight()
height_difference = new_height - old_height
if isinstance( parent_of_container, wx.ScrolledWindow ):
# fitinside is like fit, but it does the virtual size!
parent_of_container.FitInside()
tlp = self.GetTopLevelParent()
if issubclass( type( tlp ), wx.Dialog ): tlp.Fit()
def ExpandCollapse( self ): self._Change()

View File

@ -886,7 +886,7 @@ class DialogInputCustomFilterAction( Dialog ):
self._none_panel = ClientGUICommon.StaticBox( self, 'non-service actions' )
self._none_actions = wx.Choice( self._none_panel, choices = [ 'manage_tags', 'manage_ratings', 'archive', 'inbox', 'delete', 'fullscreen_switch', 'frame_back', 'frame_next', 'previous', 'next', 'first', 'last' ] )
self._none_actions = wx.Choice( self._none_panel, choices = [ 'manage_tags', 'manage_ratings', 'archive', 'inbox', 'delete', 'fullscreen_switch', 'frame_back', 'frame_next', 'previous', 'next', 'first', 'last', 'open_externally' ] )
self._ok_none = wx.Button( self._none_panel, label = 'ok' )
self._ok_none.Bind( wx.EVT_BUTTON, self.EventOKNone )
@ -2683,6 +2683,8 @@ class DialogInputLocalFiles( Dialog ):
for ( i, path ) in enumerate( file_paths ):
if path.endswith( os.path.sep + 'Thumbs.db' ) or path.endswith( os.path.sep + 'thumbs.db' ): continue
if i % 500 == 0: gc.collect()
wx.CallAfter( self.SetGaugeInfo, num_file_paths, i, u'Done ' + HC.u( i ) + '/' + HC.u( num_file_paths ) )
@ -3444,7 +3446,7 @@ class DialogInputShortcut( Dialog ):
self._shortcut = ClientGUICommon.Shortcut( self, modifier, key )
self._actions = wx.Choice( self, choices = [ 'archive', 'inbox', 'close_page', 'filter', 'fullscreen_switch', 'ratings_filter', 'frame_back', 'frame_next', 'manage_ratings', 'manage_tags', 'new_page', 'refresh', 'set_search_focus', 'show_hide_splitters', 'synchronised_wait_switch', 'previous', 'next', 'first', 'last', 'undo', 'redo' ] )
self._actions = wx.Choice( self, choices = [ 'archive', 'inbox', 'close_page', 'filter', 'fullscreen_switch', 'ratings_filter', 'frame_back', 'frame_next', 'manage_ratings', 'manage_tags', 'new_page', 'refresh', 'set_search_focus', 'show_hide_splitters', 'synchronised_wait_switch', 'previous', 'next', 'first', 'last', 'undo', 'redo', 'open_externally' ] )
self._ok = wx.Button( self, id= wx.ID_OK, label = 'Ok' )
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
@ -4452,14 +4454,9 @@ class DialogPathsToTagsRegex( Dialog ):
try:
m = re.search( regex, path )
result = re.findall( regex, path )
if m is not None:
match = m.group()
if len( match ) > 0: tags.append( match )
for match in result: tags.append( match )
except: pass
@ -4468,14 +4465,9 @@ class DialogPathsToTagsRegex( Dialog ):
try:
m = re.search( regex, path )
result = re.findall( regex, path )
if m is not None:
match = m.group()
if len( match ) > 0: tags.append( namespace + ':' + match )
for match in result: tags.append( namespace + ':' + match )
except: pass
@ -4491,6 +4483,10 @@ class DialogPathsToTagsRegex( Dialog ):
tags = HC.CleanTags( tags )
tags = list( tags )
tags.sort()
return tags
@ -5272,7 +5268,7 @@ class DialogSetupCustomFilterActions( Dialog ):
for ( key, action ) in key_dict.items():
if action in ( 'manage_tags', 'manage_ratings', 'archive', 'inbox', 'fullscreen_switch', 'frame_back', 'frame_next', 'previous', 'next', 'first', 'last', 'pan_up', 'pan_down', 'pan_left', 'pan_right' ):
if action in ( 'manage_tags', 'manage_ratings', 'archive', 'inbox', 'fullscreen_switch', 'frame_back', 'frame_next', 'previous', 'next', 'first', 'last', 'pan_up', 'pan_down', 'pan_left', 'pan_right', 'open_externally' ):
service_key = None

View File

@ -7703,7 +7703,6 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
if self._i_am_local_tag_service: self._modify_mappers.Hide()
else:
if not ( self._account.HasPermission( HC.POST_DATA ) or self._account.IsUnknownAccount() ): self._add_tag_box.Hide()
if not self._account.HasPermission( HC.MANAGE_USERS ): self._modify_mappers.Hide()
@ -7737,7 +7736,8 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
service = HC.app.GetManager( 'services' ).GetService( tag_service_key )
self._account = service.GetInfo( 'account' )
try: self._account = service.GetInfo( 'account' )
except: self._account = HC.GetUnknownAccount()
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in media ) ) )
@ -7899,7 +7899,7 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
def SetTagBoxFocus( self ):
if self._i_am_local_tag_service or self._account.HasPermission( HC.POST_DATA ) or self._account.IsUnknownAccount(): self._add_tag_box.SetFocus()
self._add_tag_box.SetFocus()

View File

@ -1222,8 +1222,8 @@ class ManagementPanelImport( ManagementPanel ):
import_controller_job_key = self._import_controller.GetJobKey( 'controller' )
import_job_key = self._import_controller.GetJobKey( 'import' )
import_queue_position_job_key = self._import_controller.GetJobKey( 'import_queue_position' )
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_builder_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
# info
@ -1247,13 +1247,13 @@ class ManagementPanelImport( ManagementPanel ):
if import_status != self._import_current_info.GetLabel(): self._import_current_info.SetLabel( import_status )
import_queue_status = import_queue_position_job_key.GetVariable( 'status' )
import_queue_status = import_queue_job_key.GetVariable( 'status' )
if import_queue_status != self._import_queue_info.GetLabel(): self._import_queue_info.SetLabel( import_queue_status )
# buttons
if import_queue_position_job_key.IsPaused():
if import_queue_job_key.IsPaused():
if self._import_pause_button.GetLabel() != 'resume':
@ -1270,7 +1270,7 @@ class ManagementPanelImport( ManagementPanel ):
if import_queue_position_job_key.IsWorking() and not import_queue_position_job_key.IsCancelled():
if import_queue_job_key.IsWorking() and not import_queue_job_key.IsCancelled():
self._import_pause_button.Enable()
self._import_cancel_button.Enable()
@ -1294,11 +1294,11 @@ class ManagementPanelImport( ManagementPanel ):
self._import_gauge.SetValue( value )
queue = import_queue_job_key.GetVariable( 'queue' )
queue = import_queue_builder_job_key.GetVariable( 'queue' )
if len( queue ) == 0:
if import_queue_job_key.IsWorking(): self._import_queue_gauge.Pulse()
if import_queue_builder_job_key.IsWorking(): self._import_queue_gauge.Pulse()
else:
self._import_queue_gauge.SetRange( 1 )
@ -1307,7 +1307,7 @@ class ManagementPanelImport( ManagementPanel ):
else:
queue_position = import_queue_position_job_key.GetVariable( 'queue_position' )
queue_position = import_queue_job_key.GetVariable( 'queue_position' )
self._import_queue_gauge.SetRange( len( queue ) )
self._import_queue_gauge.SetValue( queue_position )
@ -1316,20 +1316,20 @@ class ManagementPanelImport( ManagementPanel ):
def EventCancelImportQueue( self, event ):
import_queue_position_job_key = self._import_controller.GetJobKey( 'import_queue_position' )
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_builder_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
import_queue_position_job_key.Cancel()
import_queue_job_key.Cancel()
import_queue_builder_job_key.Cancel()
self._UpdateGUI()
def EventPauseImportQueue( self, event ):
import_queue_position_job_key = self._import_controller.GetJobKey( 'import_queue_position' )
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_position_job_key.PauseResume()
import_queue_job_key.PauseResume()
self._UpdateGUI()
@ -1340,9 +1340,9 @@ class ManagementPanelImport( ManagementPanel ):
def TestAbleToClose( self ):
import_queue_position_job_key = self._import_controller.GetJobKey( 'import_queue_position' )
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
if import_queue_position_job_key.IsWorking() and not import_queue_position_job_key.IsPaused():
if import_queue_job_key.IsWorking() and not import_queue_job_key.IsPaused():
with ClientGUIDialogs.DialogYesNo( self, 'This page is still importing. Are you sure you want to close it?' ) as dlg:
@ -1364,11 +1364,11 @@ class ManagementPanelImports( ManagementPanelImport ):
self._building_import_queue_info = wx.StaticText( self._building_import_queue_panel )
self._building_import_queue_pause_button = wx.Button( self._building_import_queue_panel, label = 'pause' )
self._building_import_queue_pause_button.Bind( wx.EVT_BUTTON, self.EventPauseBuildImportQueue )
self._building_import_queue_pause_button.Bind( wx.EVT_BUTTON, self.EventPauseImportQueueBuilder )
self._building_import_queue_pause_button.Disable()
self._building_import_queue_cancel_button = wx.Button( self._building_import_queue_panel, label = 'that\'s enough' )
self._building_import_queue_cancel_button.Bind( wx.EVT_BUTTON, self.EventCancelBuildImportQueue )
self._building_import_queue_cancel_button.Bind( wx.EVT_BUTTON, self.EventCancelImportQueueBuilder )
self._building_import_queue_cancel_button.SetForegroundColour( ( 128, 0, 0 ) )
self._building_import_queue_cancel_button.Disable()
@ -1424,20 +1424,20 @@ class ManagementPanelImports( ManagementPanelImport ):
ManagementPanelImport._UpdateGUI( self )
import_job_key = self._import_controller.GetJobKey( 'import' )
import_queue_position_job_key = self._import_controller.GetJobKey( 'import_queue_position' )
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_builder_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
# info
extend_import_queue_status = import_queue_job_key.GetVariable( 'status' )
import_queue_builder_status = import_queue_builder_job_key.GetVariable( 'status' )
if extend_import_queue_status != self._building_import_queue_info.GetLabel(): self._building_import_queue_info.SetLabel( extend_import_queue_status )
if import_queue_builder_status != self._building_import_queue_info.GetLabel(): self._building_import_queue_info.SetLabel( import_queue_builder_status )
# buttons
#
if import_queue_job_key.IsPaused():
if import_queue_builder_job_key.IsPaused():
if self._building_import_queue_pause_button.GetLabel() != 'resume':
@ -1454,7 +1454,7 @@ class ManagementPanelImports( ManagementPanelImport ):
if import_queue_job_key.IsWorking() and not import_queue_job_key.IsCancelled():
if import_queue_builder_job_key.IsWorking() and not import_queue_job_key.IsCancelled():
self._building_import_queue_pause_button.Enable()
self._building_import_queue_cancel_button.Enable()
@ -1480,19 +1480,19 @@ class ManagementPanelImports( ManagementPanelImport ):
# pending import queues
import_queues = self._import_controller.GetPendingImportQueues()
pending_import_queue_jobs = self._import_controller.GetPendingImportQueueJobs()
if import_queues != self._pending_import_queues_listbox.GetItems():
if pending_import_queue_jobs != self._pending_import_queues_listbox.GetItems():
self._pending_import_queues_listbox.SetItems( import_queues )
self._pending_import_queues_listbox.SetItems( pending_import_queue_jobs )
def EventCancelBuildImportQueue( self, event ):
def EventCancelImportQueueBuilder( self, event ):
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_builder_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
import_queue_job_key.Cancel()
import_queue_builder_job_key.Cancel()
self._UpdateGUI()
@ -1505,7 +1505,7 @@ class ManagementPanelImports( ManagementPanelImport ):
if s != '':
self._import_controller.PendImportQueue( s )
self._import_controller.PendImportQueueJob( s )
self._UpdateGUI()
@ -1515,11 +1515,11 @@ class ManagementPanelImports( ManagementPanelImport ):
else: event.Skip()
def EventPauseBuildImportQueue( self, event ):
def EventPauseImportQueueBuilder( self, event ):
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_builder_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
import_queue_job_key.PauseResume()
import_queue_builder_job_key.PauseResume()
self._UpdateGUI()
@ -1534,7 +1534,7 @@ class ManagementPanelImports( ManagementPanelImport ):
s = self._pending_import_queues_listbox.GetString( selection )
self._import_controller.MovePendingImportQueueUp( s )
self._import_controller.MovePendingImportQueueJobUp( s )
self._UpdateGUI()
@ -1551,7 +1551,7 @@ class ManagementPanelImports( ManagementPanelImport ):
s = self._pending_import_queues_listbox.GetString( selection )
self._import_controller.RemovePendingImportQueue( s )
self._import_controller.RemovePendingImportQueueJob( s )
self._UpdateGUI()
@ -1567,7 +1567,7 @@ class ManagementPanelImports( ManagementPanelImport ):
s = self._pending_import_queues_listbox.GetString( selection )
self._import_controller.MovePendingImportQueueDown( s )
self._import_controller.MovePendingImportQueueJobDown( s )
self._UpdateGUI()
@ -1679,7 +1679,10 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
self._thread_input.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
self._thread_pause_button = wx.Button( self._thread_panel, label = 'pause' )
self._thread_pause_button.Bind( wx.EVT_BUTTON, self.EventPauseBuildImportQueue )
self._thread_pause_button.Bind( wx.EVT_BUTTON, self.EventPauseImportQueueBuilder )
self._thread_manual_refresh_button = wx.Button( self._thread_panel, label = 'check now' )
self._thread_manual_refresh_button.Bind( wx.EVT_BUTTON, self.EventManualRefresh )
hbox = wx.BoxSizer( wx.HORIZONTAL )
@ -1689,10 +1692,15 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
hbox.AddF( self._thread_check_period, FLAGS_MIXED )
hbox.AddF( wx.StaticText( self._thread_panel, label = ' seconds' ), FLAGS_MIXED )
button_box = wx.BoxSizer( wx.HORIZONTAL )
button_box.AddF( self._thread_pause_button, FLAGS_EXPAND_BOTH_WAYS )
button_box.AddF( self._thread_manual_refresh_button, FLAGS_EXPAND_BOTH_WAYS )
self._thread_panel.AddF( self._thread_info, FLAGS_EXPAND_PERPENDICULAR )
self._thread_panel.AddF( self._thread_input, FLAGS_EXPAND_PERPENDICULAR )
self._thread_panel.AddF( hbox, FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._thread_panel.AddF( self._thread_pause_button, FLAGS_EXPAND_PERPENDICULAR )
self._thread_panel.AddF( button_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._thread_panel, FLAGS_EXPAND_SIZER_PERPENDICULAR )
@ -1705,13 +1713,14 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
def _SetThreadVariables( self ):
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_builder_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
thread_time = self._thread_check_period.GetValue()
thread_times_to_check = self._thread_times_to_check.GetValue()
import_queue_job_key.SetVariable( 'thread_time', thread_time )
import_queue_job_key.SetVariable( 'thread_times_to_check', thread_times_to_check )
import_queue_builder_job_key.SetVariable( 'manual_refresh', False )
import_queue_builder_job_key.SetVariable( 'thread_time', thread_time )
import_queue_builder_job_key.SetVariable( 'thread_times_to_check', thread_times_to_check )
def _UpdateGUI( self ):
@ -1719,21 +1728,21 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
ManagementPanelImport._UpdateGUI( self )
import_job_key = self._import_controller.GetJobKey( 'import' )
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_builder_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
# thread_info
status = import_queue_job_key.GetVariable( 'status' )
status = import_queue_builder_job_key.GetVariable( 'status' )
if status != self._thread_info.GetLabel(): self._thread_info.SetLabel( status )
# button
if import_queue_job_key.IsWorking():
if import_queue_builder_job_key.IsWorking():
self._thread_pause_button.Enable()
if import_queue_job_key.IsPaused():
if import_queue_builder_job_key.IsPaused():
self._thread_pause_button.SetLabel( 'resume' )
self._thread_pause_button.SetForegroundColour( ( 0, 128, 0 ) )
@ -1750,12 +1759,21 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
try:
thread_times_to_check = import_queue_job_key.GetVariable( 'thread_times_to_check' )
thread_times_to_check = import_queue_builder_job_key.GetVariable( 'thread_times_to_check' )
self._thread_times_to_check.SetValue( thread_times_to_check )
except: self._SetThreadVariables()
try:
manual_refresh = import_queue_builder_job_key.GetVariable( 'manual_refresh' )
if not import_queue_builder_job_key.IsWorking() or manual_refresh: self._thread_manual_refresh_button.Disable()
else: self._thread_manual_refresh_button.Enable()
except: self._SetThreadVariables()
def EventKeyDown( self, event ):
@ -1782,7 +1800,7 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
except: raise Exception ( 'Could not understand that url!' )
is_4chan = '4chan.org' in host
is_8chan = '8chan.co' in host
is_8chan = '8chan.co' in host or '8ch.net' in host
if not ( is_4chan or is_8chan ): raise Exception( 'This only works for 4chan and 8chan right now!' )
@ -1813,16 +1831,16 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
( board, rest_of_request ) = request[1:].split( '/res/', 1 )
json_url = url[:-4] + 'json'
image_base = 'http://8chan.co/' + board + '/src/'
image_base = 'http://8ch.net/' + board + '/src/'
except: raise Exception( 'Could not understand the board or thread id!' )
except Exception as e:
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_builder_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
import_queue_job_key.SetVariable( 'status', HC.u( e ) )
import_queue_builder_job_key.SetVariable( 'status', HC.u( e ) )
HC.ShowException( e )
@ -1833,16 +1851,25 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
self._SetThreadVariables()
self._import_controller.PendImportQueue( ( json_url, image_base ) )
self._import_controller.PendImportQueueJob( ( json_url, image_base ) )
else: event.Skip()
def EventPauseBuildImportQueue( self, event ):
def EventManualRefresh( self, event ):
import_queue_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_builder_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
import_queue_job_key.PauseResume()
import_queue_builder_job_key.SetVariable( 'manual_refresh', True )
self._thread_manual_refresh_button.Disable()
def EventPauseImportQueueBuilder( self, event ):
import_queue_builder_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
import_queue_builder_job_key.PauseResume()
self._UpdateGUI()
@ -1858,9 +1885,9 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
def TestAbleToClose( self ):
import_queue_position_job_key = self._import_controller.GetJobKey( 'import_queue' )
import_queue_builder_position_job_key = self._import_controller.GetJobKey( 'import_queue_builder' )
if self._thread_times_to_check.GetValue() > 0 and import_queue_position_job_key.IsWorking() and not import_queue_position_job_key.IsPaused():
if self._thread_times_to_check.GetValue() > 0 and import_queue_builder_position_job_key.IsWorking() and not import_queue_builder_position_job_key.IsPaused():
with ClientGUIDialogs.DialogYesNo( self, 'This page is still importing. Are you sure you want to close it?' ) as dlg:

View File

@ -469,12 +469,15 @@ class MediaPanel( ClientGUIMixins.ListeningMediaList, wx.ScrolledWindow ):
def _OpenExternally( self ):
hash = self._focussed_media.GetHash()
mime = self._focussed_media.GetMime()
path = CC.GetFilePath( hash, mime )
HC.LaunchFile( path )
if self._focussed_media is not None:
hash = self._focussed_media.GetHash()
mime = self._focussed_media.GetMime()
path = CC.GetFilePath( hash, mime )
HC.LaunchFile( path )
def _PetitionFiles( self, file_service_key ):

View File

@ -260,11 +260,11 @@ class PageImport( PageWithMedia ):
return factory
def _GenerateImportQueueGeneratorFactory( self ):
def _GenerateImportQueueBuilderFactory( self ):
def factory( job_key, item ):
return HydrusDownloading.ImportQueueGenerator( job_key, item )
return HydrusDownloading.ImportQueueBuilder( job_key, item )
return factory
@ -275,9 +275,9 @@ class PageImport( PageWithMedia ):
def _InitControllers( self ):
import_args_generator_factory = self._GenerateImportArgsGeneratorFactory()
import_queue_generator_factory = self._GenerateImportQueueGeneratorFactory()
import_queue_builder_factory = self._GenerateImportQueueBuilderFactory()
self._import_controller = HydrusDownloading.ImportController( import_args_generator_factory, import_queue_generator_factory, page_key = self._page_key )
self._import_controller = HydrusDownloading.ImportController( import_args_generator_factory, import_queue_builder_factory, page_key = self._page_key )
self._import_controller.StartDaemon()
@ -338,13 +338,13 @@ class PageImportGallery( PageImport ):
return factory
def _GenerateImportQueueGeneratorFactory( self ):
def _GenerateImportQueueBuilderFactory( self ):
def factory( job_key, item ):
downloaders_factory = self._GetDownloadersFactory()
return HydrusDownloading.ImportQueueGeneratorGallery( job_key, item, downloaders_factory )
return HydrusDownloading.ImportQueueBuilderGallery( job_key, item, downloaders_factory )
return factory
@ -471,7 +471,7 @@ class PageImportGallery( PageImport ):
if self._gallery_type == 'artist':
name = 'deviant art'
namespaces = [ 'creator', 'title', '' ]
namespaces = [ 'creator', 'title' ]
initial_search_value = 'artist username'
@ -538,7 +538,7 @@ class PageImportHDD( PageImport ):
PageImport.__init__( self, parent, initial_hashes = initial_hashes, starting_from_session = starting_from_session )
self._import_controller.PendImportQueue( self._paths_info )
self._import_controller.PendImportQueueJob( self._paths_info )
def _GenerateImportArgsGeneratorFactory( self ):
@ -582,11 +582,11 @@ class PageImportThreadWatcher( PageImport ):
return factory
def _GenerateImportQueueGeneratorFactory( self ):
def _GenerateImportQueueBuilderFactory( self ):
def factory( job_key, item ):
return HydrusDownloading.ImportQueueGeneratorThread( job_key, item )
return HydrusDownloading.ImportQueueBuilderThread( job_key, item )
return factory
@ -608,11 +608,11 @@ class PageImportURL( PageImport ):
return factory
def _GenerateImportQueueGeneratorFactory( self ):
def _GenerateImportQueueBuilderFactory( self ):
def factory( job_key, item ):
return HydrusDownloading.ImportQueueGeneratorURLs( job_key, item )
return HydrusDownloading.ImportQueueBuilderURLs( job_key, item )
return factory

View File

@ -66,7 +66,7 @@ options = {}
# Misc
NETWORK_VERSION = 15
SOFTWARE_VERSION = 143
SOFTWARE_VERSION = 144
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
@ -2228,7 +2228,7 @@ class Predicate( HydrusYAMLBase ):
( operator, value ) = info
base += u' ' + operator + u' ' + u( value )
base += u' ' + operator + u' ' + ConvertIntToPrettyString( value )
elif system_predicate_type == SYSTEM_PREDICATE_TYPE_RATIO:
@ -2261,7 +2261,7 @@ class Predicate( HydrusYAMLBase ):
value = info
base += u' is ' + u( value )
base += u' is ' + ConvertIntToPrettyString( value )
elif system_predicate_type == SYSTEM_PREDICATE_TYPE_AGE:
@ -2312,7 +2312,7 @@ class Predicate( HydrusYAMLBase ):
elif system_predicate_type == SYSTEM_PREDICATE_TYPE_SIMILAR_TO:
base = u'system:similar to '
base = u'system:similar to'
if info is not None:
@ -2323,16 +2323,14 @@ class Predicate( HydrusYAMLBase ):
elif system_predicate_type == SYSTEM_PREDICATE_TYPE_FILE_SERVICE:
base = u'system:file service'
base = u'system:'
if info is not None:
( operator, current_or_pending, service_key ) = info
base += u':'
if operator == True: base += u' is'
else: base += u' is not'
if operator == True: base += u'is'
else: base += u'is not'
if current_or_pending == PENDING: base += u' pending to '
else: base += u' currently in '
@ -2376,14 +2374,14 @@ class Predicate( HydrusYAMLBase ):
namespace = self._value
if not self._inclusive == '-': base = u'-'
if not self._inclusive: base = u'-'
else: base = u''
base += namespace + u':*'
elif self._predicate_type == PREDICATE_TYPE_WILDCARD:
( operator, wildcard ) = self._value
wildcard = self._value
if not self._inclusive: base = u'-'
else: base = u''

View File

@ -359,6 +359,7 @@ class DownloaderDeviantArt( Downloader ):
def __init__( self, artist ):
self._gallery_url = 'http://' + artist + '.deviantart.com/gallery/?catpath=/&offset='
self._artist = artist
Downloader.__init__( self )
@ -383,38 +384,18 @@ class DownloaderDeviantArt( Downloader ):
page_url = link[ 'href' ] # something in the form of blah.da.com/art/blah-123456
raw_title = link[ 'title' ] # sweet dolls by ~AngeniaC, Feb 29, 2012 in Artisan Crafts &gt; Miniatures &gt; Jewelry
raw_title = link[ 'title' ] # sweet dolls by AngeniaC, date, blah blah blah
raw_title_reversed = raw_title[::-1] # yrleweJ ;tg& serutainiM ;tg& stfarC nasitrA ni 2102 ,92 beF ,CainegnA~ yb sllod teews
raw_title_reversed = raw_title[::-1] # trAtnaiveD no CainegnA yb sllod teews
( creator_and_date_and_tags_reversed, title_reversed ) = raw_title_reversed.split( ' yb ', 1 )
creator_and_date_and_tags = creator_and_date_and_tags_reversed[::-1] # ~AngeniaC, Feb 29, 2012 in Artisan Crafts &gt; Miniatures &gt; Jewelry
( creator_with_username_char, date_and_tags ) = creator_and_date_and_tags.split( ',', 1 )
creator = creator_with_username_char[1:] # AngeniaC
( creator_and_gumpf_reversed, title_reversed ) = raw_title_reversed.split( ' yb ', 1 )
title = title_reversed[::-1] # sweet dolls
try:
( date_gumpf, raw_category_tags ) = date_and_tags.split( ' in ', 1 )
category_tags = raw_category_tags.split( ' > ' )
except Exception as e:
HC.ShowException( e )
category_tags = []
tags = []
tags.append( 'title:' + title )
tags.append( 'creator:' + creator )
tags.extend( category_tags )
tags.append( 'creator:' + self._artist )
results.append( ( page_url, tags ) )
@ -1153,6 +1134,8 @@ class ImportArgsGeneratorGallery( ImportArgsGenerator ):
service_keys_to_tags = ConvertTagsToServiceKeysToTags( tags, self._advanced_tag_options )
time.sleep( 3 )
return ( url, temp_path, service_keys_to_tags, url )
@ -1186,6 +1169,8 @@ class ImportArgsGeneratorGallery( ImportArgsGenerator ):
HC.app.Write( 'content_updates', service_keys_to_content_updates )
time.sleep( 3 )
return ( status, media_result )
@ -1280,6 +1265,8 @@ class ImportArgsGeneratorThread( ImportArgsGenerator ):
service_keys_to_tags = ConvertTagsToServiceKeysToTags( tags, self._advanced_tag_options )
time.sleep( 3 )
return ( image_url, temp_path, service_keys_to_tags, image_url )
@ -1297,6 +1284,21 @@ class ImportArgsGeneratorThread( ImportArgsGenerator ):
( media_result, ) = HC.app.ReadDaemon( 'media_results', HC.LOCAL_FILE_SERVICE_KEY, ( hash, ) )
do_tags = len( self._advanced_tag_options ) > 0
if do_tags:
tags = [ 'filename:' + filename ]
service_keys_to_tags = ConvertTagsToServiceKeysToTags( tags, self._advanced_tag_options )
service_keys_to_content_updates = ConvertServiceKeysToTagsToServiceKeysToContentUpdates( hash, service_keys_to_tags )
HC.app.Write( 'content_updates', service_keys_to_content_updates )
time.sleep( 3 )
return ( status, media_result )
else: return ( status, None )
@ -1344,17 +1346,17 @@ class ImportArgsGeneratorURLs( ImportArgsGenerator ):
class ImportController( object ):
def __init__( self, import_args_generator_factory, import_queue_generator_factory, page_key = None ):
def __init__( self, import_args_generator_factory, import_queue_builder_factory, page_key = None ):
self._controller_job_key = self._GetNewJobKey( 'controller' )
self._import_args_generator_factory = import_args_generator_factory
self._import_queue_generator_factory = import_queue_generator_factory
self._import_queue_builder_factory = import_queue_builder_factory
self._page_key = page_key
self._import_job_key = self._GetNewJobKey( 'import' )
self._import_queue_position_job_key = self._GetNewJobKey( 'import_queue_position' )
self._import_queue_job_key = self._GetNewJobKey( 'import_queue' )
self._import_queue_builder_job_key = self._GetNewJobKey( 'import_queue_builder' )
self._pending_import_queue_jobs = []
self._lock = threading.Lock()
@ -1381,11 +1383,11 @@ class ImportController( object ):
job_key.SetVariable( 'range', 1 )
job_key.SetVariable( 'value', 0 )
elif job_type == 'import_queue_position':
elif job_type == 'import_queue':
job_key.SetVariable( 'queue_position', 0 )
elif job_type == 'import_queue':
elif job_type == 'import_queue_builder':
job_key.SetVariable( 'queue', [] )
@ -1402,22 +1404,22 @@ class ImportController( object ):
if job_type == 'controller': return self._controller_job_key
elif job_type == 'import': return self._import_job_key
elif job_type == 'import_queue_position': return self._import_queue_position_job_key
elif job_type == 'import_queue': return self._import_queue_job_key
elif job_type == 'import_queue_builder': return self._import_queue_builder_job_key
def GetPendingImportQueues( self ):
def GetPendingImportQueueJobs( self ):
with self._lock: return self._pending_import_queue_jobs
def PendImportQueue( self, job ):
def PendImportQueueJob( self, job ):
with self._lock: self._pending_import_queue_jobs.append( job )
def RemovePendingImportQueue( self, job ):
def RemovePendingImportQueueJob( self, job ):
with self._lock:
@ -1425,7 +1427,7 @@ class ImportController( object ):
def MovePendingImportQueueUp( self, job ):
def MovePendingImportQueueJobUp( self, job ):
with self._lock:
@ -1443,7 +1445,7 @@ class ImportController( object ):
def MovePendingImportQueueDown( self, job ):
def MovePendingImportQueueJobDown( self, job ):
with self._lock:
@ -1472,8 +1474,8 @@ class ImportController( object ):
time.sleep( 0.1 )
self._import_job_key.Pause()
self._import_queue_position_job_key.Pause()
self._import_queue_job_key.Pause()
self._import_queue_builder_job_key.Pause()
if HC.shutdown or self._controller_job_key.IsDone(): break
@ -1482,8 +1484,8 @@ class ImportController( object ):
with self._lock:
queue_position = self._import_queue_position_job_key.GetVariable( 'queue_position' )
queue = self._import_queue_job_key.GetVariable( 'queue' )
queue_position = self._import_queue_job_key.GetVariable( 'queue_position' )
queue = self._import_queue_builder_job_key.GetVariable( 'queue' )
if self._import_job_key.IsDone():
@ -1499,23 +1501,23 @@ class ImportController( object ):
queue_position += 1
self._import_queue_position_job_key.SetVariable( 'queue_position', queue_position )
self._import_queue_job_key.SetVariable( 'queue_position', queue_position )
position_string = HC.u( queue_position + 1 ) + '/' + HC.u( len( queue ) )
if self._import_queue_position_job_key.IsPaused(): self._import_queue_position_job_key.SetVariable( 'status', 'paused at ' + position_string )
elif self._import_queue_position_job_key.IsWorking():
if self._import_queue_job_key.IsPaused(): self._import_queue_job_key.SetVariable( 'status', 'paused at ' + position_string )
elif self._import_queue_job_key.IsWorking():
if self._import_job_key.IsWorking():
self._import_queue_position_job_key.SetVariable( 'status', 'processing ' + position_string )
self._import_queue_job_key.SetVariable( 'status', 'processing ' + position_string )
else:
if queue_position < len( queue ):
self._import_queue_position_job_key.SetVariable( 'status', 'preparing ' + position_string )
self._import_queue_job_key.SetVariable( 'status', 'preparing ' + position_string )
self._import_job_key.Begin()
@ -1527,38 +1529,38 @@ class ImportController( object ):
else:
if self._import_queue_job_key.IsWorking(): self._import_queue_position_job_key.SetVariable( 'status', 'waiting for more items' )
else: self._import_queue_position_job_key.Finish()
if self._import_queue_builder_job_key.IsWorking(): self._import_queue_job_key.SetVariable( 'status', 'waiting for more items' )
else: self._import_queue_job_key.Finish()
else:
if self._import_queue_position_job_key.IsDone():
if self._import_queue_job_key.IsDone():
if self._import_queue_position_job_key.IsCancelled(): status = 'cancelled at ' + position_string
if self._import_queue_job_key.IsCancelled(): status = 'cancelled at ' + position_string
else: status = 'done'
self._import_queue_position_job_key = self._GetNewJobKey( 'import_queue_position' )
self._import_queue_job_key = self._GetNewJobKey( 'import_queue' )
self._import_queue_builder_job_key = self._GetNewJobKey( 'import_queue_builder' )
else: status = ''
self._import_queue_position_job_key.SetVariable( 'status', status )
self._import_queue_job_key.SetVariable( 'status', status )
if len( self._pending_import_queue_jobs ) > 0:
self._import_queue_position_job_key.Begin()
self._import_queue_job_key.Begin()
self._import_queue_builder_job_key.Begin()
item = self._pending_import_queue_jobs.pop( 0 )
queue_generator = self._import_queue_generator_factory( self._import_queue_job_key, item )
queue_builder = self._import_queue_builder_factory( self._import_queue_builder_job_key, item )
# make it a daemon, not a thread job, as it has a loop!
threading.Thread( target = queue_generator ).start()
threading.Thread( target = queue_builder ).start()
@ -1570,14 +1572,14 @@ class ImportController( object ):
finally:
self._import_job_key.Cancel()
self._import_queue_position_job_key.Cancel()
self._import_queue_job_key.Cancel()
self._import_queue_builder_job_key.Cancel()
def StartDaemon( self ): threading.Thread( target = self.MainLoop ).start()
class ImportQueueGenerator( object ):
class ImportQueueBuilder( object ):
def __init__( self, job_key, item ):
@ -1594,11 +1596,11 @@ class ImportQueueGenerator( object ):
self._job_key.Finish()
class ImportQueueGeneratorGallery( ImportQueueGenerator ):
class ImportQueueBuilderGallery( ImportQueueBuilder ):
def __init__( self, job_key, item, downloaders_factory ):
ImportQueueGenerator.__init__( self, job_key, item )
ImportQueueBuilder.__init__( self, job_key, item )
self._downloaders_factory = downloaders_factory
@ -1686,7 +1688,7 @@ class ImportQueueGeneratorGallery( ImportQueueGenerator ):
finally: self._job_key.Finish()
class ImportQueueGeneratorURLs( ImportQueueGenerator ):
class ImportQueueBuilderURLs( ImportQueueBuilder ):
def __call__( self ):
@ -1719,7 +1721,7 @@ class ImportQueueGeneratorURLs( ImportQueueGenerator ):
finally: self._job_key.Finish()
class ImportQueueGeneratorThread( ImportQueueGenerator ):
class ImportQueueBuilderThread( ImportQueueBuilder ):
def __call__( self ):
@ -1731,6 +1733,7 @@ class ImportQueueGeneratorThread( ImportQueueGenerator ):
image_infos_already_added = set()
first_run = True
manual_refresh = False
while True:
@ -1767,7 +1770,11 @@ class ImportQueueGeneratorThread( ImportQueueGenerator ):
next_thread_check = last_thread_check + thread_time
if next_thread_check < HC.GetNow():
manual_refresh = self._job_key.GetVariable( 'manual_refresh' )
not_too_soon_for_manual_refresh = HC.GetNow() - last_thread_check > 10
if ( manual_refresh and not_too_soon_for_manual_refresh ) or next_thread_check < HC.GetNow():
self._job_key.SetVariable( 'status', 'checking thread' )
@ -1812,6 +1819,7 @@ class ImportQueueGeneratorThread( ImportQueueGenerator ):
last_thread_check = HC.GetNow()
if first_run: first_run = False
elif manual_refresh: self._job_key.SetVariable( 'manual_refresh', False )
else:
if thread_times_to_check > 0: self._job_key.SetVariable( 'thread_times_to_check', thread_times_to_check - 1 )

View File

@ -29,9 +29,9 @@ class TestDownloaders( unittest.TestCase ):
with open( HC.STATIC_DIR + os.path.sep + 'testing' + os.path.sep + 'da_page.html' ) as f: da_page = f.read()
HC.http.SetResponse( HC.GET, 'http://sakimichan.deviantart.com/gallery/?catpath=/&offset=0', da_gallery )
HC.http.SetResponse( HC.GET, 'http://sakimichan.deviantart.com/art/Thumbs-up-411079893', da_page )
HC.http.SetResponse( HC.GET, 'http://sakimichan.deviantart.com/art/Sailor-moon-in-PJs-506918040', da_page )
HC.http.SetResponse( HC.GET, 'http://fc04.deviantart.net/fs70/f/2013/306/1/5/thumbs_up_by_sakimichan-d6sqv9x.jpg', 'image file' )
HC.http.SetResponse( HC.GET, 'http://fc00.deviantart.net/fs71/f/2015/013/3/c/3c026edbe356b22c802e7be0db6fbd0b-d8dt0go.jpg', 'image file' )
#
@ -41,15 +41,15 @@ class TestDownloaders( unittest.TestCase ):
gallery_urls = downloader.GetAnotherPage()
expected_gallery_urls = [('http://sakimichan.deviantart.com/art/Thumbs-up-411079893', ['title:Thumbs up', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Sci-Fi']), ('http://sakimichan.deviantart.com/art/The-Major-405852926', ['title:The Major', 'creator:akimichan', 'Fan Art', 'Cartoons & Comics', 'Digital', 'Movies & TV']), ('http://sakimichan.deviantart.com/art/Little-mermaid-fearie-401197163', ['title:Little mermaid fearie', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Fantasy']), ('http://sakimichan.deviantart.com/art/Get-Em-Boy-or-not-399250537', ['title:Get Em Boy..or not', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Fantasy']), ('http://sakimichan.deviantart.com/art/Welcome-To-The-Gang-398381756', ['title:Welcome To The Gang', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Fantasy']), ('http://sakimichan.deviantart.com/art/Sci-Fi-Elf-398195577', ['title:Sci-Fi Elf', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Sci-Fi']), ('http://sakimichan.deviantart.com/art/Ahri-391295844', ['title:Ahri', 'creator:akimichan', 'Fan Art', 'Cartoons & Comics', 'Digital', 'Games']), ('http://sakimichan.deviantart.com/art/Orphan-386257383', ['title:Orphan', 'creator:akimichan', 'Manga & Anime', 'Digital Media', 'Drawings']), ('http://sakimichan.deviantart.com/art/The-summon-385996679', ['title:The summon', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Fantasy']), ('http://sakimichan.deviantart.com/art/Rainbow2-377174679', ['title:Rainbow2', 'creator:akimichan', 'Manga & Anime', 'Digital Media', 'Drawings']), ('http://sakimichan.deviantart.com/art/Sword-Art-online-378517412', ['title:Sword Art online', 'creator:akimichan', 'Fan Art', 'Cartoons & Comics', 'Digital', 'Movies & TV']), ('http://sakimichan.deviantart.com/art/Adventure-Time-Group-photo-371913392', ['title:Adventure Time Group photo', 'creator:akimichan', 'Fan Art', 'Cartoons & Comics', 'Digital', 'Movies & TV']), ('http://sakimichan.deviantart.com/art/resurrection-353827147', ['title:resurrection', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Fantasy']), ('http://sakimichan.deviantart.com/art/playful-Snow-Harpy-343275265', ['title:playful Snow Harpy', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Fantasy']), ('http://sakimichan.deviantart.com/art/Link-369553015', ['title:Link', 'creator:akimichan', 'Fan Art', 'Digital Art', 'Painting & Airbrushing', 'Games']), ('http://sakimichan.deviantart.com/art/Dc-girls-363385250', ['title:Dc girls', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Fantasy']), ('http://sakimichan.deviantart.com/art/Running-With-Spirits-337981944', ['title:Running With Spirits', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Fantasy']), ('http://sakimichan.deviantart.com/art/FMA-358647977', ['title:FMA', 'creator:akimichan', 'Fan Art', 'Cartoons & Comics', 'Digital', 'Movies & TV']), ('http://sakimichan.deviantart.com/art/Wonder-woman-347864644', ['title:Wonder woman', 'creator:akimichan', 'Fan Art', 'Cartoons & Comics', 'Digital', 'Movies & TV']), ('http://sakimichan.deviantart.com/art/Ariel-found-Headphones-341927000', ['title:Ariel found Headphones', 'creator:akimichan', 'Fan Art', 'Cartoons & Comics', 'Digital', 'Movies & TV']), ('http://sakimichan.deviantart.com/art/Jack-Frost-340925669', ['title:Jack Frost', 'creator:akimichan', 'Fan Art', 'Cartoons & Comics', 'Digital', 'Movies & TV']), ('http://sakimichan.deviantart.com/art/Musics-blooming-336308163', ['title:Musics blooming', 'creator:akimichan', 'Digital Art', 'Drawings', 'Fantasy']), ('http://sakimichan.deviantart.com/art/Yin-Yang-Goddess-327641961', ['title:Yin Yang Goddess', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Fantasy']), ('http://sakimichan.deviantart.com/art/Angelof-Justice-322844340', ['title:Angelof Justice', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Fantasy'])]
expected_gallery_urls = [('http://sakimichan.deviantart.com/art/Sailor-moon-in-PJs-506918040', ['title:Sailor moon in PJs', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Johnny-Bravo-505601401', ['title:Johnny Bravo', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Daphne-505394693', ['title:Daphne !', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/kim-Possible-505195132', ['title:kim Possible', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Levi-s-evil-plan-504966437', ["title:Levi's evil plan", 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Velma-504483448', ['title:Velma', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Scoobydoo-504238131', ['title:Scoobydoo', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Kerrigan-chilling-503477012', ['title:Kerrigan chilling', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Kiki-498525851', ['title:Kiki', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Waiter-Howl-502377515', ['title:Waiter Howl', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Modern-Loki-497985045', ['title:Modern Loki', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Emma-501919103', ['title:Emma', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Lola-494941222', ['title:Lola', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Elsas-501262184', ['title:Elsas', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Tsunade-499517356', ['title:Tsunade', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/A-little-cold-out-commission-498326494', ['title:A little cold out(commission)', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Girl-496999831', ['title:Girl', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Green-elf-496797148', ['title:Green elf', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Itachi-496625357', ['title:Itachi', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Sesshomaru-495474394', ['title:Sesshomaru', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Mononoke-years-later-502160436', ['title:Mononoke years later', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Jinx-488513585', ['title:Jinx', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Alex-in-wonderland-485819661', ['title:Alex in wonderland', 'creator:sakimichan']), ('http://sakimichan.deviantart.com/art/Ariels-476991263', ['title:Ariels', 'creator:sakimichan'])]
self.assertEqual( gallery_urls, expected_gallery_urls )
#
tags = ['title:Thumbs up', 'creator:akimichan', 'Digital Art', 'Drawings & Paintings', 'Sci-Fi']
tags = ['title:Sailor moon in PJs', 'creator:sakimichan']
info = downloader.GetFileAndTags( 'http://sakimichan.deviantart.com/art/Thumbs-up-411079893', tags )
info = downloader.GetFileAndTags( 'http://sakimichan.deviantart.com/art/Sailor-moon-in-PJs-506918040', tags )
( temp_path, tags ) = info

View File

@ -388,6 +388,153 @@ class TestTagsManager( unittest.TestCase ):
self.assertEqual( self._other_tags_manager.GetPetitioned( self._reset_service_key ), set() )
class TestTagObjects( unittest.TestCase ):
def test_predicates( self ):
p = HC.Predicate( HC.PREDICATE_TYPE_TAG, 'tag' )
self.assertEqual( p.GetUnicode(), u'tag' )
p = HC.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', counts = { HC.CURRENT : 1, HC.PENDING : 2 } )
self.assertEqual( p.GetUnicode( with_count = False ), u'tag' )
self.assertEqual( p.GetUnicode( with_count = True ), u'tag (1) (+2)' )
p = HC.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', inclusive = False )
self.assertEqual( p.GetUnicode(), u'-tag' )
p = HC.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', inclusive = False, counts = { HC.CURRENT : 1, HC.PENDING : 2 } )
self.assertEqual( p.GetUnicode( with_count = False ), u'-tag' )
self.assertEqual( p.GetUnicode( with_count = True ), u'-tag (1) (+2)' )
#
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_AGE, ( '<', 1, 2, 3, 4 ) ) )
self.assertEqual( p.GetUnicode(), u'system:age < 1y2m3d4h' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_AGE, ( u'\u2248', 1, 2, 3, 4 ) ) )
self.assertEqual( p.GetUnicode(), u'system:age ' + u'\u2248' + ' 1y2m3d4h' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_AGE, ( '>', 1, 2, 3, 4 ) ) )
self.assertEqual( p.GetUnicode(), u'system:age > 1y2m3d4h' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_ARCHIVE, None ), counts = { HC.CURRENT : 1000 } )
self.assertEqual( p.GetUnicode(), u'system:archive (1,000)' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_DURATION, ( '<', 1000 ) ) )
self.assertEqual( p.GetUnicode(), u'system:duration < 1,000' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_EVERYTHING, None ), counts = { HC.CURRENT : 2000 } )
self.assertEqual( p.GetUnicode(), u'system:everything (2,000)' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_FILE_SERVICE, ( True, HC.CURRENT, HC.LOCAL_FILE_SERVICE_KEY ) ) )
self.assertEqual( p.GetUnicode(), u'system:is currently in local files' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_FILE_SERVICE, ( False, HC.PENDING, HC.LOCAL_FILE_SERVICE_KEY ) ) )
self.assertEqual( p.GetUnicode(), u'system:is not pending to local files' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_HASH, 'abcd'.decode( 'hex' ) ) )
self.assertEqual( p.GetUnicode(), u'system:hash is abcd' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_HEIGHT, ( '<', 2000 ) ) )
self.assertEqual( p.GetUnicode(), u'system:height < 2,000' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_INBOX, None ), counts = { HC.CURRENT : 1000 } )
self.assertEqual( p.GetUnicode(), u'system:inbox (1,000)' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_LIMIT, 2000 ) )
self.assertEqual( p.GetUnicode(), u'system:limit is 2,000' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_LOCAL, None ), counts = { HC.CURRENT : 100 } )
self.assertEqual( p.GetUnicode(), u'system:local (100)' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_MIME, HC.IMAGES ) )
self.assertEqual( p.GetUnicode(), u'system:mime is image' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_MIME, HC.VIDEO_WEBM ) )
self.assertEqual( p.GetUnicode(), u'system:mime is video/webm' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_NOT_LOCAL, None ), counts = { HC.CURRENT : 100 } )
self.assertEqual( p.GetUnicode(), u'system:not local (100)' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_NUM_TAGS, ( '<', 2 ) ) )
self.assertEqual( p.GetUnicode(), u'system:number of tags < 2' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_NUM_WORDS, ( '<', 5000 ) ) )
self.assertEqual( p.GetUnicode(), u'system:number of words < 5,000' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_RATING, ( HC.LOCAL_FILE_SERVICE_KEY, '>', 0.2 ) ) )
self.assertEqual( p.GetUnicode(), u'system:rating for local files > 0.2' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_RATIO, ( '=', 16, 9 ) ) )
self.assertEqual( p.GetUnicode(), u'system:ratio = 16:9' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_SIMILAR_TO, ( 'abcd'.decode( 'hex' ), 5 ) ) )
self.assertEqual( p.GetUnicode(), u'system:similar to abcd using max hamming of 5' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_SIZE, ( '>', 5, 1048576 ) ) )
self.assertEqual( p.GetUnicode(), u'system:size > 5MB' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_UNTAGGED, HC.IMAGES ) )
self.assertEqual( p.GetUnicode(), u'system:untagged' )
p = HC.Predicate( HC.PREDICATE_TYPE_SYSTEM, ( HC.SYSTEM_PREDICATE_TYPE_WIDTH, ( '=', 1920 ) ) )
self.assertEqual( p.GetUnicode(), u'system:width = 1,920' )
#
p = HC.Predicate( HC.PREDICATE_TYPE_NAMESPACE, 'series' )
self.assertEqual( p.GetUnicode(), u'series:*' )
p = HC.Predicate( HC.PREDICATE_TYPE_TAG, 'series', inclusive = False )
self.assertEqual( p.GetUnicode(), u'-series' )
#
p = HC.Predicate( HC.PREDICATE_TYPE_WILDCARD, 'a*i:o*' )
self.assertEqual( p.GetUnicode(), u'a*i:o*' )
p = HC.Predicate( HC.PREDICATE_TYPE_TAG, 'a*i:o*', inclusive = False )
self.assertEqual( p.GetUnicode(), u'-a*i:o*' )
#
p = HC.Predicate( HC.PREDICATE_TYPE_PARENT, 'series:game of thrones' )
self.assertEqual( p.GetUnicode(), u' series:game of thrones' )
class TestTagParents( unittest.TestCase ):
@classmethod

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long