Version 108
This commit is contained in:
parent
3339e5e0be
commit
c14d8f4008
|
@ -8,6 +8,19 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 108</h3></li>
|
||||
<ul>
|
||||
<li>added 'database->delete orphan files' for manual firing of this maintenance routine</li>
|
||||
<li>improved redirect location parsing--should fix the booru 'Could not connect to None!' errors</li>
|
||||
<li>fixed 4chan thread downloader's erroneous 'still working' warning when trying to close while paused</li>
|
||||
<li>added multiple additions to manage tag parents dialog</li>
|
||||
<li>added multiple additions to manage tag siblings dialog</li>
|
||||
<li>generic improvements to both dialogs</li>
|
||||
<li>manage tags dialog now shows all the selection's tags combined, not just the tag intersection</li>
|
||||
<li>multiple improvements to manage tags dialog</li>
|
||||
<li>reworked some tagsbox classes to be more flexible</li>
|
||||
<li>improved dialogsetupexport's use of tagsbox</li>
|
||||
</ul>
|
||||
<li><h3>version 107</h3></li>
|
||||
<ul>
|
||||
<li>converted 'namespace blacklists' to more general 'tag censorship'</li>
|
||||
|
|
|
@ -1538,6 +1538,8 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
|
|||
|
||||
c.execute( 'REPLACE INTO shutdown_timestamps ( shutdown_type, timestamp ) VALUES ( ?, ? );', ( CC.SHUTDOWN_TIMESTAMP_DELETE_ORPHANS, HC.GetNow() ) )
|
||||
|
||||
HC.ShowText( 'orphaned files deleted' )
|
||||
|
||||
|
||||
def _DeletePending( self, c, service_identifier ):
|
||||
|
||||
|
|
|
@ -570,6 +570,16 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
|||
HC.pubsub.pub( 'notify_new_undo' )
|
||||
|
||||
|
||||
def _DeleteOrphans( self ):
|
||||
|
||||
message = 'This will iterate through the client\'s file store, deleting anything that is no longer needed. It happens automatically every few days, but you can force it here. If you have a lot of files, it will take a few minutes. A popup message will appear when it is done.'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES: HC.app.Write( 'delete_orphans' )
|
||||
|
||||
|
||||
|
||||
def _DeletePending( self, service_identifier ):
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'Are you sure you want to delete the pending data for ' + service_identifier.GetName() + '?' ) as dlg:
|
||||
|
@ -781,6 +791,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
|||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'restore_database' ), p( 'Restore Database Backup' ), p( 'Restore the database from an external location.' ) )
|
||||
menu.AppendSeparator()
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'vacuum_db' ), p( '&Vacuum' ), p( 'Rebuild the Database.' ) )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_orphans' ), p( '&Delete Orphan Files' ), p( 'Go through the client\'s file store, deleting any files that are no longer needed.' ) )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'regenerate_thumbnails' ), p( '&Regenerate All Thumbnails' ), p( 'Delete all thumbnails and regenerate from original files.' ) )
|
||||
menu.AppendSeparator()
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'clear_caches' ), p( '&Clear Caches' ), p( 'Fully clear the fullscreen, preview and thumbnail caches.' ) )
|
||||
|
@ -1628,7 +1639,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
def _VacuumDatabase( self ):
|
||||
|
||||
message = 'This will rebuild the database, rewriting all indices and tables to be contiguous, optimising most operations. If you have a large database, it will take some time. A popup message will appear when it is done.'
|
||||
message = 'This will rebuild the database, rewriting all indices and tables to be contiguous and optimising most operations. It happens automatically every few days, but you can force it here. If you have a large database, it will take a few minutes. A popup message will appear when it is done.'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
||||
|
||||
|
@ -1783,6 +1794,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
HC.pubsub.pub( 'notify_new_sessions' )
|
||||
|
||||
elif command == 'delete_orphans': self._DeleteOrphans()
|
||||
elif command == 'delete_pending': self._DeletePending( data )
|
||||
elif command == 'exit': self.EventExit( event )
|
||||
elif command == 'fetch_ip': self._FetchIP( data )
|
||||
|
|
|
@ -4137,34 +4137,29 @@ class TagsBoxColourOptions( TagsBox ):
|
|||
|
||||
|
||||
|
||||
class TagsBoxCPP( TagsBox ):
|
||||
class TagsBoxCounts( TagsBox ):
|
||||
|
||||
has_counts = True
|
||||
|
||||
def __init__( self, parent, page_key ):
|
||||
def __init__( self, parent ):
|
||||
|
||||
TagsBox.__init__( self, parent, min_height = 200 )
|
||||
|
||||
self._sort = HC.options[ 'default_tag_sort' ]
|
||||
|
||||
self._page_key = page_key
|
||||
|
||||
self._tag_service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER
|
||||
self._last_media = None
|
||||
|
||||
self._tag_service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER
|
||||
|
||||
self._current_tags_to_count = collections.Counter()
|
||||
self._deleted_tags_to_count = collections.Counter()
|
||||
self._pending_tags_to_count = collections.Counter()
|
||||
self._petitioned_tags_to_count = collections.Counter()
|
||||
|
||||
HC.pubsub.sub( self, 'SetTagsByMedia', 'new_tags_selection' )
|
||||
HC.pubsub.sub( self, 'ChangeTagRepository', 'change_tag_repository' )
|
||||
|
||||
|
||||
def _Activate( self, s, term ):
|
||||
|
||||
predicate = HC.Predicate( HC.PREDICATE_TYPE_TAG, ( '+', term ), None )
|
||||
|
||||
HC.pubsub.pub( 'add_predicate', self._page_key, predicate )
|
||||
self._show_current = True
|
||||
self._show_deleted = False
|
||||
self._show_pending = True
|
||||
self._show_petitioned = True
|
||||
|
||||
|
||||
def _GetAllTagsForClipboard( self, with_counts = False ):
|
||||
|
@ -4177,11 +4172,12 @@ class TagsBoxCPP( TagsBox ):
|
|||
|
||||
siblings_manager = HC.app.GetManager( 'tag_siblings' )
|
||||
|
||||
all_current = ( tag for tag in self._current_tags_to_count if self._current_tags_to_count[ tag ] > 0 )
|
||||
all_pending = ( tag for tag in self._pending_tags_to_count if self._pending_tags_to_count[ tag ] > 0 )
|
||||
all_petitioned = ( tag for tag in self._petitioned_tags_to_count if self._petitioned_tags_to_count[ tag ] > 0 )
|
||||
all_tags = set()
|
||||
|
||||
all_tags = set( itertools.chain( all_current, all_pending, all_petitioned ) )
|
||||
if self._show_current: all_tags.update( ( tag for ( tag, count ) in self._current_tags_to_count.items() if count > 0 ) )
|
||||
if self._show_deleted: all_tags.update( ( tag for ( tag, count ) in self._deleted_tags_to_count.items() if count > 0 ) )
|
||||
if self._show_pending: all_tags.update( ( tag for ( tag, count ) in self._pending_tags_to_count.items() if count > 0 ) )
|
||||
if self._show_petitioned: all_tags.update( ( tag for ( tag, count ) in self._petitioned_tags_to_count.items() if count > 0 ) )
|
||||
|
||||
self._ordered_strings = []
|
||||
self._strings_to_terms = {}
|
||||
|
@ -4190,9 +4186,10 @@ class TagsBoxCPP( TagsBox ):
|
|||
|
||||
tag_string = tag
|
||||
|
||||
if tag in self._current_tags_to_count: tag_string += ' (' + HC.ConvertIntToPrettyString( self._current_tags_to_count[ tag ] ) + ')'
|
||||
if tag in self._pending_tags_to_count: tag_string += ' (+' + HC.ConvertIntToPrettyString( self._pending_tags_to_count[ tag ] ) + ')'
|
||||
if tag in self._petitioned_tags_to_count: tag_string += ' (-' + HC.ConvertIntToPrettyString( self._petitioned_tags_to_count[ tag ] ) + ')'
|
||||
if self._show_current and tag in self._current_tags_to_count: tag_string += ' (' + HC.ConvertIntToPrettyString( self._current_tags_to_count[ tag ] ) + ')'
|
||||
if self._show_pending and tag in self._pending_tags_to_count: tag_string += ' (+' + HC.ConvertIntToPrettyString( self._pending_tags_to_count[ tag ] ) + ')'
|
||||
if self._show_petitioned and tag in self._petitioned_tags_to_count: tag_string += ' (-' + HC.ConvertIntToPrettyString( self._petitioned_tags_to_count[ tag ] ) + ')'
|
||||
if self._show_deleted and tag in self._deleted_tags_to_count: tag_string += ' (X' + HC.ConvertIntToPrettyString( self._deleted_tags_to_count[ tag ] ) + ')'
|
||||
|
||||
sibling = siblings_manager.GetSibling( tag )
|
||||
|
||||
|
@ -4225,14 +4222,11 @@ class TagsBoxCPP( TagsBox ):
|
|||
self._TextsHaveChanged()
|
||||
|
||||
|
||||
def ChangeTagRepository( self, page_key, service_identifier ):
|
||||
def ChangeTagRepository( self, service_identifier ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self._tag_service_identifier = service_identifier
|
||||
|
||||
if self._last_media is not None: self.SetTagsByMedia( self._page_key, self._last_media )
|
||||
|
||||
self._tag_service_identifier = service_identifier
|
||||
|
||||
if self._last_media is not None: self.SetTagsByMedia( self._last_media )
|
||||
|
||||
|
||||
def SetSort( self, sort ):
|
||||
|
@ -4242,70 +4236,120 @@ class TagsBoxCPP( TagsBox ):
|
|||
self._SortTags()
|
||||
|
||||
|
||||
def SetTags( self, current_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ):
|
||||
def SetTags( self, current_tags_to_count = collections.Counter(), deleted_tags_to_count = collections.Counter(), pending_tags_to_count = collections.Counter(), petitioned_tags_to_count = collections.Counter() ):
|
||||
|
||||
siblings_manager = HC.app.GetManager( 'tag_siblings' )
|
||||
|
||||
current_tags_to_count = siblings_manager.CollapseTagsToCount( current_tags_to_count )
|
||||
|
||||
self._current_tags_to_count = current_tags_to_count
|
||||
self._deleted_tags_to_count = deleted_tags_to_count
|
||||
self._pending_tags_to_count = pending_tags_to_count
|
||||
self._petitioned_tags_to_count = petitioned_tags_to_count
|
||||
|
||||
self._RecalcStrings()
|
||||
|
||||
|
||||
def SetTagsByMedia( self, page_key, media, force_reload = False ):
|
||||
def SetShow( self, type, value ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
media = set( media )
|
||||
|
||||
if force_reload:
|
||||
|
||||
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = CC.GetMediasTagCount( media, self._tag_service_identifier )
|
||||
|
||||
self.SetTags( current_tags_to_count, pending_tags_to_count, petitioned_tags_to_count )
|
||||
|
||||
else:
|
||||
|
||||
if self._last_media is None: ( removees, adds ) = ( set(), media )
|
||||
else:
|
||||
|
||||
removees = self._last_media.difference( media )
|
||||
adds = media.difference( self._last_media )
|
||||
|
||||
|
||||
siblings_manager = HC.app.GetManager( 'tag_siblings' )
|
||||
|
||||
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = CC.GetMediasTagCount( removees, self._tag_service_identifier )
|
||||
|
||||
current_tags_to_count = siblings_manager.CollapseTagsToCount( current_tags_to_count )
|
||||
|
||||
self._current_tags_to_count.subtract( current_tags_to_count )
|
||||
self._pending_tags_to_count.subtract( pending_tags_to_count )
|
||||
self._petitioned_tags_to_count.subtract( petitioned_tags_to_count )
|
||||
|
||||
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = CC.GetMediasTagCount( adds, self._tag_service_identifier )
|
||||
|
||||
current_tags_to_count = siblings_manager.CollapseTagsToCount( current_tags_to_count )
|
||||
|
||||
self._current_tags_to_count.update( current_tags_to_count )
|
||||
self._pending_tags_to_count.update( pending_tags_to_count )
|
||||
self._petitioned_tags_to_count.update( petitioned_tags_to_count )
|
||||
|
||||
|
||||
self._last_media = media
|
||||
|
||||
self._RecalcStrings()
|
||||
|
||||
if type == 'current': self._show_current = value
|
||||
elif type == 'deleted': self._show_deleted = value
|
||||
elif type == 'pending': self._show_pending = value
|
||||
elif type == 'petitioned': self._show_petitioned = value
|
||||
|
||||
self._RecalcStrings()
|
||||
|
||||
|
||||
class TagsBoxCPPWithSorter( StaticBox ):
|
||||
def SetTagsByMedia( self, media, force_reload = False ):
|
||||
|
||||
media = set( media )
|
||||
|
||||
if force_reload:
|
||||
|
||||
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = CC.GetMediasTagCount( media, self._tag_service_identifier )
|
||||
|
||||
self.SetTags( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count )
|
||||
|
||||
else:
|
||||
|
||||
if self._last_media is None: ( removees, adds ) = ( set(), media )
|
||||
else:
|
||||
|
||||
removees = self._last_media.difference( media )
|
||||
adds = media.difference( self._last_media )
|
||||
|
||||
|
||||
siblings_manager = HC.app.GetManager( 'tag_siblings' )
|
||||
|
||||
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = CC.GetMediasTagCount( removees, self._tag_service_identifier )
|
||||
|
||||
current_tags_to_count = siblings_manager.CollapseTagsToCount( current_tags_to_count )
|
||||
|
||||
self._current_tags_to_count.subtract( current_tags_to_count )
|
||||
self._deleted_tags_to_count.subtract( deleted_tags_to_count )
|
||||
self._pending_tags_to_count.subtract( pending_tags_to_count )
|
||||
self._petitioned_tags_to_count.subtract( petitioned_tags_to_count )
|
||||
|
||||
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = CC.GetMediasTagCount( adds, self._tag_service_identifier )
|
||||
|
||||
current_tags_to_count = siblings_manager.CollapseTagsToCount( current_tags_to_count )
|
||||
|
||||
self._current_tags_to_count.update( current_tags_to_count )
|
||||
self._deleted_tags_to_count.update( deleted_tags_to_count )
|
||||
self._pending_tags_to_count.update( pending_tags_to_count )
|
||||
self._petitioned_tags_to_count.update( petitioned_tags_to_count )
|
||||
|
||||
|
||||
self._last_media = media
|
||||
|
||||
self._RecalcStrings()
|
||||
|
||||
|
||||
class TagsBoxCPP( TagsBoxCounts ):
|
||||
|
||||
def __init__( self, parent, page_key ):
|
||||
|
||||
StaticBox.__init__( self, parent, 'selection tags' )
|
||||
TagsBoxCounts.__init__( self, parent )
|
||||
|
||||
self._page_key = page_key
|
||||
|
||||
HC.pubsub.sub( self, 'SetTagsByMediaPubsub', 'new_tags_selection' )
|
||||
HC.pubsub.sub( self, 'ChangeTagRepositoryPubsub', 'change_tag_repository' )
|
||||
|
||||
|
||||
def _Activate( self, s, term ):
|
||||
|
||||
predicate = HC.Predicate( HC.PREDICATE_TYPE_TAG, ( '+', term ), None )
|
||||
|
||||
HC.pubsub.pub( 'add_predicate', self._page_key, predicate )
|
||||
|
||||
|
||||
def ChangeTagRepositoryPubsub( self, page_key, service_identifier ):
|
||||
|
||||
if page_key == self._page_key: self.ChangeTagRepository( service_identifier )
|
||||
|
||||
|
||||
def SetTagsByMediaPubsub( self, page_key, media, force_reload = False ):
|
||||
|
||||
if page_key == self._page_key: self.SetTagsByMedia( media, force_reload = force_reload )
|
||||
|
||||
|
||||
class TagsBoxCountsSimple( TagsBoxCounts ):
|
||||
|
||||
def __init__( self, parent, callable ):
|
||||
|
||||
TagsBoxCounts.__init__( self, parent )
|
||||
|
||||
self._callable = callable
|
||||
|
||||
|
||||
def _Activate( self, s, term ): self._callable( term )
|
||||
|
||||
class TagsBoxCountsSorter( StaticBox ):
|
||||
|
||||
def __init__( self, parent, title ):
|
||||
|
||||
StaticBox.__init__( self, parent, title )
|
||||
|
||||
self._sorter = wx.Choice( self )
|
||||
|
||||
|
@ -4321,12 +4365,11 @@ class TagsBoxCPPWithSorter( StaticBox ):
|
|||
|
||||
self._sorter.Bind( wx.EVT_CHOICE, self.EventSort )
|
||||
|
||||
self._tags_box = TagsBoxCPP( self, page_key )
|
||||
|
||||
self.AddF( self._sorter, FLAGS_EXPAND_PERPENDICULAR )
|
||||
self.AddF( self._tags_box, FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
|
||||
def ChangeTagRepository( self, service_identifier ): self._tags_box.ChangeTagRepository( service_identifier )
|
||||
|
||||
def EventSort( self, event ):
|
||||
|
||||
selection = self._sorter.GetSelection()
|
||||
|
@ -4339,6 +4382,15 @@ class TagsBoxCPPWithSorter( StaticBox ):
|
|||
|
||||
|
||||
|
||||
def SetTagsBox( self, tags_box ):
|
||||
|
||||
self._tags_box = tags_box
|
||||
|
||||
self.AddF( self._tags_box, FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
|
||||
def SetTagsByMedia( self, media, force_reload = False ): self._tags_box.SetTagsByMedia( media, force_reload = force_reload )
|
||||
|
||||
class TagsBoxFlat( TagsBox ):
|
||||
|
||||
def __init__( self, parent, removed_callable ):
|
||||
|
@ -4394,7 +4446,7 @@ class TagsBoxFlat( TagsBox ):
|
|||
|
||||
|
||||
|
||||
def AddTag( self, tag, parents ):
|
||||
def AddTag( self, tag, parents = [] ):
|
||||
|
||||
if tag in self._tags: self._tags.discard( tag )
|
||||
else:
|
||||
|
@ -4411,7 +4463,9 @@ class TagsBoxFlat( TagsBox ):
|
|||
|
||||
def SetTags( self, tags ):
|
||||
|
||||
self._tags = tags
|
||||
self._tags = set()
|
||||
|
||||
for tag in tags: self._tags.add( tag )
|
||||
|
||||
self._RecalcTags()
|
||||
|
||||
|
|
|
@ -281,6 +281,77 @@ class DialogAdvancedContentUpdate( Dialog ):
|
|||
self._specific_tag.SetLabel( tag )
|
||||
|
||||
|
||||
class DialogButtonChoice( Dialog ):
|
||||
|
||||
def __init__( self, parent, intro, choices ):
|
||||
|
||||
def InitialiseControls():
|
||||
|
||||
self._hidden_cancel = wx.Button( self, id = wx.ID_CANCEL, size = ( 0, 0 ) )
|
||||
|
||||
self._buttons = []
|
||||
self._ids_to_data = {}
|
||||
|
||||
i = 0
|
||||
|
||||
for ( text, data ) in choices:
|
||||
|
||||
self._buttons.append( wx.Button( self, label = text, id = i ) )
|
||||
|
||||
self._ids_to_data[ i ] = data
|
||||
|
||||
i += 1
|
||||
|
||||
|
||||
|
||||
def PopulateControls():
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def ArrangeControls():
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = intro ), FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
for button in self._buttons: vbox.AddF( button, FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
( x, y ) = self.GetEffectiveMinSize()
|
||||
|
||||
self.SetInitialSize( ( x, y ) )
|
||||
|
||||
|
||||
Dialog.__init__( self, parent, 'choose what to do', position = 'center' )
|
||||
|
||||
InitialiseControls()
|
||||
|
||||
PopulateControls()
|
||||
|
||||
ArrangeControls()
|
||||
|
||||
self.Bind( wx.EVT_BUTTON, self.EventButton )
|
||||
|
||||
wx.CallAfter( self._buttons[0].SetFocus )
|
||||
|
||||
|
||||
def EventButton( self, event ):
|
||||
|
||||
id = event.GetId()
|
||||
|
||||
if id == wx.ID_CANCEL: self.EndModal( wx.ID_CANCEL )
|
||||
else:
|
||||
|
||||
self._data = self._ids_to_data[ id ]
|
||||
|
||||
self.EndModal( wx.ID_OK )
|
||||
|
||||
|
||||
|
||||
def GetData( self ): return self._data
|
||||
|
||||
class DialogChooseNewServiceMethod( Dialog ):
|
||||
|
||||
def __init__( self, parent ):
|
||||
|
@ -4940,7 +5011,12 @@ class DialogSetupExport( Dialog ):
|
|||
|
||||
def InitialiseControls():
|
||||
|
||||
self._tags_box = ClientGUICommon.TagsBoxCPPWithSorter( self, self._page_key )
|
||||
self._tags_box = ClientGUICommon.TagsBoxCountsSorter( self, 'files\' tags' )
|
||||
|
||||
t = ClientGUICommon.TagsBoxCounts( self._tags_box )
|
||||
|
||||
self._tags_box.SetTagsBox( t )
|
||||
|
||||
self._tags_box.SetMinSize( ( 220, 300 ) )
|
||||
|
||||
self._paths = ClientGUICommon.SaneListCtrl( self, 480, [ ( 'number', 60 ), ( 'mime', 70 ), ( 'expected path', -1 ) ] )
|
||||
|
@ -5054,8 +5130,6 @@ class DialogSetupExport( Dialog ):
|
|||
|
||||
Dialog.__init__( self, parent, 'setup export' )
|
||||
|
||||
self._page_key = os.urandom( 32 )
|
||||
|
||||
InitialiseControls()
|
||||
|
||||
PopulateControls()
|
||||
|
@ -5218,7 +5292,7 @@ class DialogSetupExport( Dialog ):
|
|||
all_media = [ media for ( ( ordering_index, media ), mime, old_path ) in [ self._paths.GetClientData( index ) for index in indices ] ]
|
||||
|
||||
|
||||
HC.pubsub.pub( 'new_tags_selection', self._page_key, all_media )
|
||||
self._tags_box.SetTagsByMedia( all_media )
|
||||
|
||||
|
||||
class DialogYesNo( Dialog ):
|
||||
|
|
|
@ -7,6 +7,7 @@ import ClientConstants as CC
|
|||
import ClientConstantsMessages
|
||||
import ClientGUICommon
|
||||
import ClientGUIDialogs
|
||||
import ClientGUIMixins
|
||||
import collections
|
||||
import HydrusNATPunch
|
||||
import itertools
|
||||
|
@ -5761,19 +5762,19 @@ class DialogManageTagCensorship( ClientGUIDialogs.Dialog ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
intro = 'Here you can set which tags or classes of tags you do not want to see.'
|
||||
intro += os.linesep
|
||||
intro += "Input ':' for all namespaced tags, and '' for all unnamespaced tags."
|
||||
intro += os.linesep
|
||||
intro += 'You may have to refresh your current queries to see any changes.'
|
||||
intro = "Here you can set which tags or classes of tags you do not want to see. Input something like 'series:' to censor an entire namespace, or ':' for all namespaced tags, and '' for all unnamespaced tags. You may have to refresh your current queries to see any changes."
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = intro ), FLAGS_EXPAND_PERPENDICULAR )
|
||||
st = wx.StaticText( self, label = intro )
|
||||
|
||||
st.Wrap( 350 )
|
||||
|
||||
vbox.AddF( st, FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._tag_services, FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( buttons, FLAGS_BUTTON_SIZERS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
self.SetInitialSize( ( 350, 480 ) )
|
||||
self.SetInitialSize( ( -1, 480 ) )
|
||||
|
||||
|
||||
ClientGUIDialogs.Dialog.__init__( self, parent, 'tag censorship' )
|
||||
|
@ -6038,11 +6039,13 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
self._tag_parents.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
|
||||
self._tag_parents.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
|
||||
|
||||
self._child_text = wx.StaticText( self )
|
||||
self._parent_text = wx.StaticText( self )
|
||||
removed_callable = lambda tag: 1
|
||||
|
||||
self._child_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.SetChild, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
||||
self._parent_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.SetParent, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
||||
self._children = ClientGUICommon.TagsBoxFlat( self, removed_callable )
|
||||
self._parents = ClientGUICommon.TagsBoxFlat( self, removed_callable )
|
||||
|
||||
self._child_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddChild, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
||||
self._parent_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddParent, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
||||
|
||||
self._add = wx.Button( self, label = 'add' )
|
||||
self._add.Bind( wx.EVT_BUTTON, self.EventAddButton )
|
||||
|
@ -6060,15 +6063,17 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._tag_parents.SortListItems( 2 )
|
||||
|
||||
if tag is not None: self.SetChild( tag )
|
||||
if tag is not None: self.AddChild( tag )
|
||||
|
||||
|
||||
def ArrangeControls():
|
||||
|
||||
text_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||
intro = 'Files with a tag on the left will also be given the tag on the right.'
|
||||
|
||||
text_box.AddF( self._child_text, FLAGS_EXPAND_BOTH_WAYS )
|
||||
text_box.AddF( self._parent_text, FLAGS_EXPAND_BOTH_WAYS )
|
||||
tags_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
tags_box.AddF( self._children, FLAGS_EXPAND_BOTH_WAYS )
|
||||
tags_box.AddF( self._parents, FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
input_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
|
@ -6077,9 +6082,10 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = intro ), FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._tag_parents, FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( self._add, FLAGS_LONE_BUTTON )
|
||||
vbox.AddF( text_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.AddF( tags_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.AddF( input_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
@ -6104,9 +6110,6 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._pairs_to_reasons = {}
|
||||
|
||||
self._current_parent = None
|
||||
self._current_child = None
|
||||
|
||||
InitialiseControls()
|
||||
|
||||
PopulateControls()
|
||||
|
@ -6245,7 +6248,7 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
if HydrusTags.LoopInSimpleChildrenToParents( simple_children_to_parents, potential_child, potential_parent ):
|
||||
|
||||
wx.MessageBox( 'Adding that pair would create a loop!' )
|
||||
wx.MessageBox( 'Adding ' + potential_child + '->' + potential_parent + ' would create a loop!' )
|
||||
|
||||
return False
|
||||
|
||||
|
@ -6256,10 +6259,34 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def _SetButtonStatus( self ):
|
||||
|
||||
if self._current_parent is None or self._current_child is None: self._add.Disable()
|
||||
if len( self._children.GetTags() ) == 0 or len( self._parents.GetTags() ) == 0: self._add.Disable()
|
||||
else: self._add.Enable()
|
||||
|
||||
|
||||
def AddChild( self, tag, parents = [] ):
|
||||
|
||||
if tag is not None:
|
||||
|
||||
if tag in self._parents.GetTags(): self._parents.AddTag( tag )
|
||||
|
||||
self._children.AddTag( tag )
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
|
||||
def AddParent( self, tag, parents = [] ):
|
||||
|
||||
if tag is not None:
|
||||
|
||||
if tag in self._children.GetTags(): self._children.AddTag( tag )
|
||||
|
||||
self._parents.AddTag( tag )
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
|
||||
def EventActivated( self, event ):
|
||||
|
||||
all_selected = self._tag_parents.GetAllSelected()
|
||||
|
@ -6276,13 +6303,15 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def EventAddButton( self, event ):
|
||||
|
||||
if self._current_child is not None and self._current_parent is not None:
|
||||
|
||||
self._AddPair( self._current_child, self._current_parent )
|
||||
|
||||
self.SetChild( None )
|
||||
self.SetParent( None )
|
||||
|
||||
children = self._children.GetTags()
|
||||
parents = self._parents.GetTags()
|
||||
|
||||
for ( child, parent ) in itertools.product( children, parents ): self._AddPair( child, parent )
|
||||
|
||||
self._children.SetTags( [] )
|
||||
self._parents.SetTags( [] )
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
def EventItemSelected( self, event ):
|
||||
|
@ -6325,33 +6354,9 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
return ( self._service_identifier, content_updates )
|
||||
|
||||
|
||||
def SetChild( self, tag, parents = [] ):
|
||||
|
||||
if tag is not None and tag == self._current_parent: self.SetParent( None )
|
||||
|
||||
self._current_child = tag
|
||||
|
||||
if tag is None: self._child_text.SetLabel( '' )
|
||||
else: self._child_text.SetLabel( tag )
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
def SetParent( self, tag, parents = [] ):
|
||||
|
||||
if tag is not None and tag == self._current_child: self.SetChild( None )
|
||||
|
||||
self._current_parent = tag
|
||||
|
||||
if tag is None: self._parent_text.SetLabel( '' )
|
||||
else: self._parent_text.SetLabel( tag )
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
def SetTagBoxFocus( self ):
|
||||
|
||||
if self._current_child is None: self._child_input.SetFocus()
|
||||
if len( self._children.GetTags() ) == 0: self._child_input.SetFocus()
|
||||
else: self._parent_input.SetFocus()
|
||||
|
||||
|
||||
|
@ -6496,10 +6501,12 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
self._tag_siblings.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
|
||||
self._tag_siblings.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
|
||||
|
||||
self._old_text = wx.StaticText( self )
|
||||
self._new_text = wx.StaticText( self )
|
||||
removed_callable = lambda tags: 1
|
||||
|
||||
self._old_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.SetOld, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
||||
self._old_siblings = ClientGUICommon.TagsBoxFlat( self, removed_callable )
|
||||
self._new_sibling = wx.StaticText( self )
|
||||
|
||||
self._old_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddOld, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
||||
self._new_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.SetNew, HC.LOCAL_FILE_SERVICE_IDENTIFIER, service_identifier )
|
||||
|
||||
self._add = wx.Button( self, label = 'add' )
|
||||
|
@ -6518,15 +6525,23 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._tag_siblings.SortListItems( 2 )
|
||||
|
||||
if tag is not None: self.SetOld( tag )
|
||||
if tag is not None: self.AddOld( tag )
|
||||
|
||||
|
||||
def ArrangeControls():
|
||||
|
||||
intro = 'Tags on the left will be replaced by those on the right.'
|
||||
|
||||
new_sibling_box = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
new_sibling_box.AddF( ( 10, 10 ), FLAGS_EXPAND_BOTH_WAYS )
|
||||
new_sibling_box.AddF( self._new_sibling, FLAGS_EXPAND_PERPENDICULAR )
|
||||
new_sibling_box.AddF( ( 10, 10 ), FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
text_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
text_box.AddF( self._old_text, FLAGS_EXPAND_BOTH_WAYS )
|
||||
text_box.AddF( self._new_text, FLAGS_EXPAND_BOTH_WAYS )
|
||||
text_box.AddF( self._old_siblings, FLAGS_EXPAND_BOTH_WAYS )
|
||||
text_box.AddF( new_sibling_box, FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
|
||||
input_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
|
@ -6535,6 +6550,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = intro ), FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._tag_siblings, FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( self._add, FLAGS_LONE_BUTTON )
|
||||
vbox.AddF( text_box, FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
@ -6562,7 +6578,6 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._pairs_to_reasons = {}
|
||||
|
||||
self._current_old = None
|
||||
self._current_new = None
|
||||
|
||||
InitialiseControls()
|
||||
|
@ -6571,8 +6586,6 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
ArrangeControls()
|
||||
|
||||
if tag is not None: self.SetOld( tag )
|
||||
|
||||
|
||||
def _AddPair( self, old, new ):
|
||||
|
||||
|
@ -6718,7 +6731,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
if next_new == potential_old:
|
||||
|
||||
wx.MessageBox( 'Adding that pair would create a loop!' )
|
||||
wx.MessageBox( 'Adding ' + potential_old + '->' + potential_new + ' would create a loop!' )
|
||||
|
||||
return False
|
||||
|
||||
|
@ -6730,10 +6743,56 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def _SetButtonStatus( self ):
|
||||
|
||||
if self._current_new is None or self._current_old is None: self._add.Disable()
|
||||
if self._current_new is None or len( self._old_siblings.GetTags() ) == 0: self._add.Disable()
|
||||
else: self._add.Enable()
|
||||
|
||||
|
||||
def AddOld( self, old, parents = [] ):
|
||||
|
||||
if old is not None:
|
||||
|
||||
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
|
||||
|
||||
current_olds = { current_old for ( current_old, current_new ) in current_pairs }
|
||||
|
||||
# test for ambiguity
|
||||
|
||||
while old in current_olds:
|
||||
|
||||
olds_to_news = dict( current_pairs )
|
||||
|
||||
new = olds_to_news[ old ]
|
||||
|
||||
message = 'There already is a relationship set for ' + old + '! It goes to ' + new + '.'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'I want to overwrite it', no_label = 'do nothing' ) as dlg:
|
||||
|
||||
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
||||
|
||||
if dlg.ShowModal() != wx.ID_YES: return
|
||||
|
||||
self._AddPair( old, new )
|
||||
|
||||
|
||||
|
||||
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
|
||||
|
||||
current_olds = { current_old for ( current_old, current_new ) in current_pairs }
|
||||
|
||||
|
||||
|
||||
#
|
||||
|
||||
if old is not None:
|
||||
|
||||
if old == self._current_new: self.SetNew( None )
|
||||
|
||||
self._old_siblings.AddTag( old )
|
||||
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
def EventActivated( self, event ):
|
||||
|
||||
all_selected = self._tag_siblings.GetAllSelected()
|
||||
|
@ -6750,13 +6809,15 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def EventAddButton( self, event ):
|
||||
|
||||
if self._current_old is not None and self._current_new is not None:
|
||||
if self._current_new is not None and len( self._old_siblings.GetTags() ) > 0:
|
||||
|
||||
self._AddPair( self._current_old, self._current_new )
|
||||
for old in self._old_siblings.GetTags(): self._AddPair( old, self._current_new )
|
||||
|
||||
self.SetOld( None )
|
||||
self._old_siblings.SetTags( [] )
|
||||
self.SetNew( None )
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
|
||||
def EventItemSelected( self, event ):
|
||||
|
@ -6803,65 +6864,22 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def SetNew( self, new, parents = [] ):
|
||||
|
||||
if new is not None and new == self._current_old: self.SetOld( None )
|
||||
if new is None: self._new_sibling.SetLabel( '' )
|
||||
else:
|
||||
|
||||
if new in self._old_siblings.GetTags(): self._old_siblings.AddTag( new )
|
||||
|
||||
self._new_sibling.SetLabel( new )
|
||||
|
||||
|
||||
self._current_new = new
|
||||
|
||||
if new is None: self._new_text.SetLabel( '' )
|
||||
else: self._new_text.SetLabel( new )
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
def SetOld( self, old, parents = [] ):
|
||||
|
||||
if old is not None:
|
||||
|
||||
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
|
||||
|
||||
current_olds = { current_old for ( current_old, current_new ) in current_pairs }
|
||||
|
||||
# test for ambiguity
|
||||
|
||||
while old in current_olds:
|
||||
|
||||
olds_to_news = dict( current_pairs )
|
||||
|
||||
new = olds_to_news[ old ]
|
||||
|
||||
message = 'There already is a relationship set for ' + old + '! It goes to ' + new + '.'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message, yes_label = 'I want to overwrite it', no_label = 'do nothing' ) as dlg:
|
||||
|
||||
if self._service_identifier != HC.LOCAL_TAG_SERVICE_IDENTIFIER:
|
||||
|
||||
if dlg.ShowModal() != wx.ID_YES: return
|
||||
|
||||
self._AddPair( old, new )
|
||||
|
||||
|
||||
|
||||
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
|
||||
|
||||
current_olds = { current_old for ( current_old, current_new ) in current_pairs }
|
||||
|
||||
|
||||
|
||||
#
|
||||
|
||||
if old is not None and old == self._current_new: self.SetNew( None )
|
||||
|
||||
self._current_old = old
|
||||
|
||||
if old is None: self._old_text.SetLabel( '' )
|
||||
else: self._old_text.SetLabel( old )
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
def SetTagBoxFocus( self ):
|
||||
|
||||
if self._current_old is None: self._old_input.SetFocus()
|
||||
if len( self._old_siblings.GetTags() ) == 0: self._old_input.SetFocus()
|
||||
else: self._new_input.SetFocus()
|
||||
|
||||
|
||||
|
@ -7150,7 +7168,16 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def InitialiseControls():
|
||||
|
||||
self._tags_box = ClientGUICommon.TagsBoxManageWithShowDeleted( self, self.AddTag, self._current_tags, self._deleted_tags, self._pending_tags, self._petitioned_tags )
|
||||
self._tags_box_sorter = ClientGUICommon.TagsBoxCountsSorter( self, 'tags' )
|
||||
|
||||
self._tags_box = ClientGUICommon.TagsBoxCountsSimple( self._tags_box_sorter, self.AddTag )
|
||||
|
||||
self._tags_box_sorter.SetTagsBox( self._tags_box )
|
||||
|
||||
self._show_deleted_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'show deleted' )
|
||||
self._show_deleted_checkbox.Bind( wx.EVT_CHECKBOX, self.EventShowDeleted )
|
||||
|
||||
self._tags_box_sorter.AddF( self._show_deleted_checkbox, FLAGS_LONE_BUTTON )
|
||||
|
||||
self._add_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddTag, self._file_service_identifier, self._tag_service_identifier )
|
||||
|
||||
|
@ -7166,7 +7193,9 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def PopulateControls():
|
||||
|
||||
pass
|
||||
self._tags_box.ChangeTagRepository( self._tag_service_identifier )
|
||||
|
||||
self._tags_box.SetTagsByMedia( self._media )
|
||||
|
||||
|
||||
def ArrangeControls():
|
||||
|
@ -7187,7 +7216,7 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._tags_box, FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( self._tags_box_sorter, FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( self._add_tag_box, FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( copy_paste_hbox, FLAGS_BUTTON_SIZERS )
|
||||
vbox.AddF( self._modify_mappers, FLAGS_BUTTON_SIZERS )
|
||||
|
@ -7213,12 +7242,14 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
|
|||
self._account = service.GetAccount()
|
||||
|
||||
|
||||
tags_managers = [ m.GetTagsManager() for m in media ]
|
||||
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in media ) ) )
|
||||
|
||||
( self._current_tags, self._deleted_tags, self._pending_tags, self._petitioned_tags ) = CC.IntersectTags( tags_managers, tag_service_identifier )
|
||||
media_results = HC.app.Read( 'media_results', self._file_service_identifier, hashes )
|
||||
|
||||
self._current_tags.sort()
|
||||
self._pending_tags.sort()
|
||||
# this should now be a nice clean copy of the original media
|
||||
self._media = [ ClientGUIMixins.MediaSingleton( media_result ) for media_result in media_results ]
|
||||
|
||||
tags_managers = [ m.GetTagsManager() for m in self._media ]
|
||||
|
||||
InitialiseControls()
|
||||
|
||||
|
@ -7229,100 +7260,74 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def _AddTag( self, tag, only_add = False ):
|
||||
|
||||
tag_managers = [ m.GetTagsManager() for m in self._media ]
|
||||
|
||||
num_files = len( self._media )
|
||||
|
||||
num_current = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetCurrent( self._tag_service_identifier ) ] )
|
||||
|
||||
choices = []
|
||||
|
||||
if self._i_am_local_tag_service:
|
||||
|
||||
if tag in self._pending_tags:
|
||||
|
||||
if only_add: return
|
||||
|
||||
self._pending_tags.remove( tag )
|
||||
|
||||
self._tags_box.RescindPend( tag )
|
||||
|
||||
elif tag in self._petitioned_tags:
|
||||
|
||||
self._petitioned_tags.remove( tag )
|
||||
|
||||
self._tags_box.RescindPetition( tag )
|
||||
|
||||
elif tag in self._current_tags:
|
||||
|
||||
if only_add: return
|
||||
|
||||
self._petitioned_tags.append( tag )
|
||||
|
||||
self._tags_box.PetitionTag( tag )
|
||||
|
||||
else:
|
||||
|
||||
self._pending_tags.append( tag )
|
||||
|
||||
self._tags_box.PendTag( tag )
|
||||
|
||||
|
||||
self._content_updates = []
|
||||
|
||||
self._content_updates.extend( [ HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( tag, self._hashes ) ) for tag in self._pending_tags ] )
|
||||
self._content_updates.extend( [ HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE, ( tag, self._hashes ) ) for tag in self._petitioned_tags ] )
|
||||
if num_current < num_files: choices.append( ( 'add ' + tag, HC.CONTENT_UPDATE_ADD ) )
|
||||
if num_current > 0: choices.append( ( 'delete ' + tag, HC.CONTENT_UPDATE_DELETE ) )
|
||||
|
||||
else:
|
||||
|
||||
if tag in self._pending_tags:
|
||||
num_pending = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetPending( self._tag_service_identifier ) ] )
|
||||
num_petitioned = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetPetitioned( self._tag_service_identifier ) ] )
|
||||
|
||||
if num_current + num_pending < num_files: choices.append( ( 'pend ' + tag, HC.CONTENT_UPDATE_PENDING ) )
|
||||
if num_current > num_petitioned: choices.append( ( 'petition ' + tag, HC.CONTENT_UPDATE_PETITION ) )
|
||||
if num_pending > 0: choices.append( ( 'rescind pending ' + tag, HC.CONTENT_UPDATE_RESCIND_PENDING ) )
|
||||
if num_petitioned > 0: choices.append( ( 'rescind petitioned ' + tag, HC.CONTENT_UPDATE_RESCIND_PETITION ) )
|
||||
|
||||
|
||||
if len( choices ) > 1:
|
||||
|
||||
intro = 'What would you like to do?'
|
||||
|
||||
with ClientGUIDialogs.DialogButtonChoice( self, intro, choices ) as dlg:
|
||||
|
||||
if only_add: return
|
||||
|
||||
self._pending_tags.remove( tag )
|
||||
|
||||
self._tags_box.RescindPend( tag )
|
||||
|
||||
self._content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_RESCIND_PENDING, ( tag, self._hashes ) ) )
|
||||
|
||||
elif tag in self._petitioned_tags:
|
||||
|
||||
self._petitioned_tags.remove( tag )
|
||||
|
||||
self._tags_box.RescindPetition( tag )
|
||||
|
||||
self._content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_RESCIND_PETITION, ( tag, self._hashes ) ) )
|
||||
|
||||
elif tag in self._current_tags:
|
||||
|
||||
if only_add: return
|
||||
|
||||
if self._account.HasPermission( HC.RESOLVE_PETITIONS ):
|
||||
|
||||
self._content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_PETITION, ( tag, self._hashes, 'admin' ) ) )
|
||||
|
||||
self._petitioned_tags.append( tag )
|
||||
|
||||
self._tags_box.PetitionTag( tag )
|
||||
|
||||
elif self._account.HasPermission( HC.POST_PETITIONS ):
|
||||
|
||||
message = 'Enter a reason for this tag to be removed. A janitor will review your petition.'
|
||||
|
||||
with wx.TextEntryDialog( self, message ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
self._content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_PETITION, ( tag, self._hashes, dlg.GetValue() ) ) )
|
||||
|
||||
self._petitioned_tags.append( tag )
|
||||
|
||||
self._tags_box.PetitionTag( tag )
|
||||
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
self._content_updates.append( HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, HC.CONTENT_UPDATE_PENDING, ( tag, self._hashes ) ) )
|
||||
|
||||
self._pending_tags.append( tag )
|
||||
|
||||
self._tags_box.PendTag( tag )
|
||||
if dlg.ShowModal() == wx.ID_OK: choice = dlg.GetData()
|
||||
else: return
|
||||
|
||||
|
||||
else: [ ( text, choice ) ] = choices
|
||||
|
||||
if choice == HC.CONTENT_UPDATE_ADD: media_to_affect = ( m for m in self._media if tag not in m.GetTagsManager().GetCurrent( self._tag_service_identifier ) )
|
||||
elif choice == HC.CONTENT_UPDATE_DELETE: media_to_affect = ( m for m in self._media if tag in m.GetTagsManager().GetCurrent( self._tag_service_identifier ) )
|
||||
elif choice == HC.CONTENT_UPDATE_PENDING: media_to_affect = ( m for m in self._media if tag not in m.GetTagsManager().GetCurrent( self._tag_service_identifier ) and tag not in m.GetTagsManager().GetPending( self._tag_service_identifier ) )
|
||||
elif choice == HC.CONTENT_UPDATE_PETITION: media_to_affect = ( m for m in self._media if tag in m.GetTagsManager().GetCurrent( self._tag_service_identifier ) and tag not in m.GetTagsManager().GetPetitioned( self._tag_service_identifier ) )
|
||||
elif choice == HC.CONTENT_UPDATE_RESCIND_PENDING: media_to_affect = ( m for m in self._media if tag in m.GetTagsManager().GetPending( self._tag_service_identifier ) )
|
||||
elif choice == HC.CONTENT_UPDATE_RESCIND_PETITION: media_to_affect = ( m for m in self._media if tag in m.GetTagsManager().GetPetitioned( self._tag_service_identifier ) )
|
||||
|
||||
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in media_to_affect ) ) )
|
||||
|
||||
if choice == HC.CONTENT_UPDATE_PETITION:
|
||||
|
||||
if self._account.HasPermission( HC.RESOLVE_PETITIONS ): reason = 'admin'
|
||||
else:
|
||||
|
||||
message = 'Enter a reason for this tag to be removed. A janitor will review your petition.'
|
||||
|
||||
with wx.TextEntryDialog( self, message ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK: reason = dlg.GetValue()
|
||||
else: return
|
||||
|
||||
|
||||
|
||||
content_update = HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, choice, ( tag, hashes, reason ) )
|
||||
|
||||
else: content_update = HC.ContentUpdate( HC.CONTENT_DATA_TYPE_MAPPINGS, choice, ( tag, hashes ) )
|
||||
|
||||
for m in self._media: m.GetMediaResult().ProcessContentUpdate( self._tag_service_identifier, content_update )
|
||||
|
||||
self._content_updates.append( content_update )
|
||||
|
||||
self._tags_box.SetTagsByMedia( self._media, force_reload = True )
|
||||
|
||||
|
||||
def AddTag( self, tag, parents = [] ):
|
||||
|
@ -7340,7 +7345,9 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
|
|||
|
||||
if wx.TheClipboard.Open():
|
||||
|
||||
tags = self._current_tags + self._pending_tags
|
||||
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = CC.GetMediasTagCount( self._media, self._tag_service_identifier )
|
||||
|
||||
tags = set( current_tags_to_count.keys() ).union( pending_tags_to_count.keys() )
|
||||
|
||||
text = yaml.safe_dump( tags )
|
||||
|
||||
|
@ -7357,7 +7364,7 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
|
|||
|
||||
tag = self._tags_box.GetSelectedTag()
|
||||
|
||||
if tag is not None and tag in self._current_tags or tag in self._petitioned_tags:
|
||||
if tag is not None:
|
||||
|
||||
subject_identifiers = [ HC.AccountIdentifier( hash = hash, tag = tag ) for hash in self._hashes ]
|
||||
|
||||
|
@ -7381,20 +7388,16 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
|
|||
|
||||
tags = yaml.safe_load( text )
|
||||
|
||||
tags = [ tag for tag in tags if tag not in self._current_tags and tag not in self._pending_tags ]
|
||||
|
||||
for tag in tags: self.AddTag( tag )
|
||||
for tag in tags: self._AddTag( tag, only_add = True )
|
||||
|
||||
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 EventTagsBoxAction( self, event ):
|
||||
def EventShowDeleted( self, event ):
|
||||
|
||||
tag = self._tags_box.GetSelectedTag()
|
||||
|
||||
if tag is not None: self.AddTag( tag )
|
||||
self._tags_box.SetShow( 'deleted', self._show_deleted_checkbox.GetValue() )
|
||||
|
||||
|
||||
def GetContentUpdates( self ): return ( self._tag_service_identifier, self._content_updates )
|
||||
|
|
|
@ -375,7 +375,11 @@ class ManagementPanel( wx.lib.scrolledpanel.ScrolledPanel ):
|
|||
|
||||
def _MakeCurrentSelectionTagsBox( self, sizer ):
|
||||
|
||||
tags_box = ClientGUICommon.TagsBoxCPPWithSorter( self, self._page_key )
|
||||
tags_box = ClientGUICommon.TagsBoxCountsSorter( self, 'selection tags' )
|
||||
|
||||
t = ClientGUICommon.TagsBoxCPP( tags_box, self._page_key )
|
||||
|
||||
tags_box.SetTagsBox( t )
|
||||
|
||||
sizer.AddF( tags_box, FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
|
@ -1786,6 +1790,19 @@ class ManagementPanelImportThreadWatcher( ManagementPanelImport ):
|
|||
if page_key == self._page_key: self._thread_input.SetFocus()
|
||||
|
||||
|
||||
def TestAbleToClose( self ):
|
||||
|
||||
import_queue_position_job_key = self._import_controller.GetJobKey( 'import_queue' )
|
||||
|
||||
if import_queue_position_job_key.IsWorking() and not import_queue_position_job_key.IsPaused():
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'This page is still importing. Are you sure you want to close it?' ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_NO: raise Exception()
|
||||
|
||||
|
||||
|
||||
|
||||
class ManagementPanelPetitions( ManagementPanel ):
|
||||
|
||||
def __init__( self, parent, page, page_key, file_service_identifier, petition_service_identifier, starting_from_session = False ):
|
||||
|
@ -2017,8 +2034,8 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
HC.pubsub.sub( self, 'AddMediaResultsFromQuery', 'add_media_results_from_query' )
|
||||
HC.pubsub.sub( self, 'AddPredicate', 'add_predicate' )
|
||||
HC.pubsub.sub( self, 'ChangeFileRepository', 'change_file_repository' )
|
||||
HC.pubsub.sub( self, 'ChangeTagRepository', 'change_tag_repository' )
|
||||
HC.pubsub.sub( self, 'ChangeFileRepositoryPubsub', 'change_file_repository' )
|
||||
HC.pubsub.sub( self, 'ChangeTagRepositoryPubsub', 'change_tag_repository' )
|
||||
HC.pubsub.sub( self, 'IncludeCurrent', 'notify_include_current' )
|
||||
HC.pubsub.sub( self, 'IncludePending', 'notify_include_pending' )
|
||||
HC.pubsub.sub( self, 'SearchImmediately', 'notify_search_immediately' )
|
||||
|
@ -2094,7 +2111,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
|
||||
|
||||
def ChangeFileRepository( self, page_key, service_identifier ):
|
||||
def ChangeFileRepositoryPubsub( self, page_key, service_identifier ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
|
@ -2104,7 +2121,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
|
||||
|
||||
def ChangeTagRepository( self, page_key, service_identifier ):
|
||||
def ChangeTagRepositoryPubsub( self, page_key, service_identifier ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
|
|
|
@ -439,13 +439,9 @@ class MediaPanel( ClientGUIMixins.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
if len( self._selected_media ) > 0:
|
||||
|
||||
try:
|
||||
|
||||
with ClientGUIDialogsManage.DialogManageTags( None, self._file_service_identifier, self._selected_media ) as dlg: dlg.ShowModal()
|
||||
|
||||
self.SetFocus()
|
||||
|
||||
except: wx.MessageBox( traceback.format_exc() )
|
||||
with ClientGUIDialogsManage.DialogManageTags( None, self._file_service_identifier, self._selected_media ) as dlg: dlg.ShowModal()
|
||||
|
||||
self.SetFocus()
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ TEMP_DIR = BASE_DIR + os.path.sep + 'temp'
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 13
|
||||
SOFTWARE_VERSION = 107
|
||||
SOFTWARE_VERSION = 108
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
|
|
@ -1304,217 +1304,6 @@ class ImportArgsGeneratorURLs( ImportArgsGenerator ):
|
|||
else: return ( status, None )
|
||||
|
||||
|
||||
class ImportQueueGenerator():
|
||||
|
||||
def __init__( self, job_key, item ):
|
||||
|
||||
self._job_key = job_key
|
||||
self._item = item
|
||||
|
||||
|
||||
def __call__( self ):
|
||||
|
||||
queue = self._item
|
||||
|
||||
self._job_key.SetVariable( 'queue', queue )
|
||||
|
||||
self._job_key.Finish()
|
||||
|
||||
|
||||
class ImportQueueGeneratorGallery( ImportQueueGenerator ):
|
||||
|
||||
def __init__( self, job_key, item, downloaders_factory ):
|
||||
|
||||
ImportQueueGenerator.__init__( self, job_key, item )
|
||||
|
||||
self._downloaders_factory = downloaders_factory
|
||||
|
||||
|
||||
def __call__( self ):
|
||||
|
||||
try:
|
||||
|
||||
raw_query = self._item
|
||||
|
||||
downloaders = list( self._downloaders_factory( raw_query ) )
|
||||
|
||||
downloaders[0].SetupGallerySearch() # for now this is cookie-based for hf, so only have to do it on one
|
||||
|
||||
total_urls_found = 0
|
||||
|
||||
while True:
|
||||
|
||||
downloaders_to_remove = []
|
||||
|
||||
for downloader in downloaders:
|
||||
|
||||
if self._job_key.IsPaused():
|
||||
|
||||
self._job_key.SetVariable( 'status', 'paused after ' + HC.u( total_urls_found ) + ' urls' )
|
||||
|
||||
self._job_key.WaitOnPause()
|
||||
|
||||
|
||||
if self._job_key.IsCancelled(): break
|
||||
|
||||
self._job_key.SetVariable( 'status', 'found ' + HC.u( total_urls_found ) + ' urls' )
|
||||
|
||||
page_of_url_args = downloader.GetAnotherPage()
|
||||
|
||||
total_urls_found += len( page_of_url_args )
|
||||
|
||||
if len( page_of_url_args ) == 0: downloaders_to_remove.append( downloader )
|
||||
else:
|
||||
|
||||
queue = self._job_key.GetVariable( 'queue' )
|
||||
|
||||
queue = list( queue )
|
||||
|
||||
queue.extend( page_of_url_args )
|
||||
|
||||
self._job_key.SetVariable( 'queue', queue )
|
||||
|
||||
|
||||
|
||||
for downloader in downloaders_to_remove: downloaders.remove( downloader )
|
||||
|
||||
if len( downloaders ) == 0: break
|
||||
|
||||
if self._job_key.IsPaused():
|
||||
|
||||
self._job_key.SetVariable( 'status', 'paused after ' + HC.u( total_urls_found ) + ' urls' )
|
||||
|
||||
self._job_key.WaitOnPause()
|
||||
|
||||
|
||||
if self._job_key.IsCancelled(): break
|
||||
|
||||
|
||||
self._job_key.SetVariable( 'status', '' )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
self._job_key.SetVariable( 'status', HC.u( e ) )
|
||||
|
||||
HC.ShowException( e )
|
||||
|
||||
time.sleep( 2 )
|
||||
|
||||
finally: self._job_key.Finish()
|
||||
|
||||
|
||||
class ImportQueueGeneratorURLs( ImportQueueGenerator ):
|
||||
|
||||
def __call__( self ):
|
||||
|
||||
try:
|
||||
|
||||
url = self._item
|
||||
|
||||
self._job_key.SetVariable( 'status', 'Connecting to address' )
|
||||
|
||||
try: html = HC.http.Request( HC.GET, url )
|
||||
except: raise Exception( 'Could not download that url' )
|
||||
|
||||
self._job_key.SetVariable( 'status', 'parsing html' )
|
||||
|
||||
try: urls = ParsePageForURLs( html, url )
|
||||
except: raise Exception( 'Could not parse that URL\'s html' )
|
||||
|
||||
queue = urls
|
||||
|
||||
self._job_key.SetVariable( 'queue', queue )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
self._job_key.SetVariable( 'status', HC.u( e ) )
|
||||
|
||||
HC.ShowException( e )
|
||||
|
||||
time.sleep( 2 )
|
||||
|
||||
finally: self._job_key.Finish()
|
||||
|
||||
|
||||
class ImportQueueGeneratorThread( ImportQueueGenerator ):
|
||||
|
||||
def __call__( self ):
|
||||
|
||||
try:
|
||||
|
||||
( board, thread_id ) = self._item
|
||||
|
||||
last_thread_check = 0
|
||||
image_infos_already_added = set()
|
||||
|
||||
while True:
|
||||
|
||||
if self._job_key.IsPaused():
|
||||
|
||||
self._job_key.SetVariable( 'status', 'paused' )
|
||||
|
||||
self._job_key.WaitOnPause()
|
||||
|
||||
|
||||
if self._job_key.IsCancelled(): break
|
||||
|
||||
thread_time = self._job_key.GetVariable( 'thread_time' )
|
||||
|
||||
if thread_time < 30: thread_time = 30
|
||||
|
||||
next_thread_check = last_thread_check + thread_time
|
||||
|
||||
if next_thread_check < HC.GetNow():
|
||||
|
||||
self._job_key.SetVariable( 'status', 'checking thread' )
|
||||
|
||||
url = 'http://api.4chan.org/' + board + '/res/' + thread_id + '.json'
|
||||
|
||||
try:
|
||||
|
||||
raw_json = HC.http.Request( HC.GET, url )
|
||||
|
||||
json_dict = json.loads( raw_json )
|
||||
|
||||
posts_list = json_dict[ 'posts' ]
|
||||
|
||||
image_infos = [ ( post[ 'md5' ].decode( 'base64' ), board, HC.u( post[ 'tim' ] ), post[ 'ext' ], post[ 'filename' ] ) for post in posts_list if 'md5' in post ]
|
||||
|
||||
image_infos_i_can_add = [ image_info for image_info in image_infos if image_info not in image_infos_already_added ]
|
||||
|
||||
image_infos_already_added.update( image_infos_i_can_add )
|
||||
|
||||
if len( image_infos_i_can_add ) > 0:
|
||||
|
||||
queue = self._job_key.GetVariable( 'queue' )
|
||||
|
||||
queue = list( queue )
|
||||
|
||||
queue.extend( image_infos_i_can_add )
|
||||
|
||||
self._job_key.SetVariable( 'queue', queue )
|
||||
|
||||
|
||||
except HydrusExceptions.NotFoundException: raise Exception( 'Thread 404' )
|
||||
|
||||
last_thread_check = HC.GetNow()
|
||||
|
||||
else: self._job_key.SetVariable( 'status', 'rechecking thread ' + HC.ConvertTimestampToPrettyPending( next_thread_check ) )
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
self._job_key.SetVariable( 'status', HC.u( e ) )
|
||||
|
||||
HC.ShowException( e )
|
||||
|
||||
time.sleep( 2 )
|
||||
|
||||
finally: self._job_key.Finish()
|
||||
|
||||
|
||||
|
||||
class ImportController():
|
||||
|
||||
def __init__( self, import_args_generator_factory, import_queue_generator_factory, page_key = None ):
|
||||
|
@ -1748,6 +1537,216 @@ class ImportController():
|
|||
threading.Thread( target = self.MainLoop ).start()
|
||||
|
||||
|
||||
class ImportQueueGenerator():
|
||||
|
||||
def __init__( self, job_key, item ):
|
||||
|
||||
self._job_key = job_key
|
||||
self._item = item
|
||||
|
||||
|
||||
def __call__( self ):
|
||||
|
||||
queue = self._item
|
||||
|
||||
self._job_key.SetVariable( 'queue', queue )
|
||||
|
||||
self._job_key.Finish()
|
||||
|
||||
|
||||
class ImportQueueGeneratorGallery( ImportQueueGenerator ):
|
||||
|
||||
def __init__( self, job_key, item, downloaders_factory ):
|
||||
|
||||
ImportQueueGenerator.__init__( self, job_key, item )
|
||||
|
||||
self._downloaders_factory = downloaders_factory
|
||||
|
||||
|
||||
def __call__( self ):
|
||||
|
||||
try:
|
||||
|
||||
raw_query = self._item
|
||||
|
||||
downloaders = list( self._downloaders_factory( raw_query ) )
|
||||
|
||||
downloaders[0].SetupGallerySearch() # for now this is cookie-based for hf, so only have to do it on one
|
||||
|
||||
total_urls_found = 0
|
||||
|
||||
while True:
|
||||
|
||||
downloaders_to_remove = []
|
||||
|
||||
for downloader in downloaders:
|
||||
|
||||
if self._job_key.IsPaused():
|
||||
|
||||
self._job_key.SetVariable( 'status', 'paused after ' + HC.u( total_urls_found ) + ' urls' )
|
||||
|
||||
self._job_key.WaitOnPause()
|
||||
|
||||
|
||||
if self._job_key.IsCancelled(): break
|
||||
|
||||
self._job_key.SetVariable( 'status', 'found ' + HC.u( total_urls_found ) + ' urls' )
|
||||
|
||||
page_of_url_args = downloader.GetAnotherPage()
|
||||
|
||||
total_urls_found += len( page_of_url_args )
|
||||
|
||||
if len( page_of_url_args ) == 0: downloaders_to_remove.append( downloader )
|
||||
else:
|
||||
|
||||
queue = self._job_key.GetVariable( 'queue' )
|
||||
|
||||
queue = list( queue )
|
||||
|
||||
queue.extend( page_of_url_args )
|
||||
|
||||
self._job_key.SetVariable( 'queue', queue )
|
||||
|
||||
|
||||
|
||||
for downloader in downloaders_to_remove: downloaders.remove( downloader )
|
||||
|
||||
if len( downloaders ) == 0: break
|
||||
|
||||
if self._job_key.IsPaused():
|
||||
|
||||
self._job_key.SetVariable( 'status', 'paused after ' + HC.u( total_urls_found ) + ' urls' )
|
||||
|
||||
self._job_key.WaitOnPause()
|
||||
|
||||
|
||||
if self._job_key.IsCancelled(): break
|
||||
|
||||
|
||||
self._job_key.SetVariable( 'status', '' )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
self._job_key.SetVariable( 'status', HC.u( e ) )
|
||||
|
||||
HC.ShowException( e )
|
||||
|
||||
time.sleep( 2 )
|
||||
|
||||
finally: self._job_key.Finish()
|
||||
|
||||
|
||||
class ImportQueueGeneratorURLs( ImportQueueGenerator ):
|
||||
|
||||
def __call__( self ):
|
||||
|
||||
try:
|
||||
|
||||
url = self._item
|
||||
|
||||
self._job_key.SetVariable( 'status', 'Connecting to address' )
|
||||
|
||||
try: html = HC.http.Request( HC.GET, url )
|
||||
except: raise Exception( 'Could not download that url' )
|
||||
|
||||
self._job_key.SetVariable( 'status', 'parsing html' )
|
||||
|
||||
try: urls = ParsePageForURLs( html, url )
|
||||
except: raise Exception( 'Could not parse that URL\'s html' )
|
||||
|
||||
queue = urls
|
||||
|
||||
self._job_key.SetVariable( 'queue', queue )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
self._job_key.SetVariable( 'status', HC.u( e ) )
|
||||
|
||||
HC.ShowException( e )
|
||||
|
||||
time.sleep( 2 )
|
||||
|
||||
finally: self._job_key.Finish()
|
||||
|
||||
|
||||
class ImportQueueGeneratorThread( ImportQueueGenerator ):
|
||||
|
||||
def __call__( self ):
|
||||
|
||||
try:
|
||||
|
||||
( board, thread_id ) = self._item
|
||||
|
||||
last_thread_check = 0
|
||||
image_infos_already_added = set()
|
||||
|
||||
while True:
|
||||
|
||||
if self._job_key.IsPaused():
|
||||
|
||||
self._job_key.SetVariable( 'status', 'paused' )
|
||||
|
||||
self._job_key.WaitOnPause()
|
||||
|
||||
|
||||
if self._job_key.IsCancelled(): break
|
||||
|
||||
thread_time = self._job_key.GetVariable( 'thread_time' )
|
||||
|
||||
if thread_time < 30: thread_time = 30
|
||||
|
||||
next_thread_check = last_thread_check + thread_time
|
||||
|
||||
if next_thread_check < HC.GetNow():
|
||||
|
||||
self._job_key.SetVariable( 'status', 'checking thread' )
|
||||
|
||||
url = 'http://api.4chan.org/' + board + '/res/' + thread_id + '.json'
|
||||
|
||||
try:
|
||||
|
||||
raw_json = HC.http.Request( HC.GET, url )
|
||||
|
||||
json_dict = json.loads( raw_json )
|
||||
|
||||
posts_list = json_dict[ 'posts' ]
|
||||
|
||||
image_infos = [ ( post[ 'md5' ].decode( 'base64' ), board, HC.u( post[ 'tim' ] ), post[ 'ext' ], post[ 'filename' ] ) for post in posts_list if 'md5' in post ]
|
||||
|
||||
image_infos_i_can_add = [ image_info for image_info in image_infos if image_info not in image_infos_already_added ]
|
||||
|
||||
image_infos_already_added.update( image_infos_i_can_add )
|
||||
|
||||
if len( image_infos_i_can_add ) > 0:
|
||||
|
||||
queue = self._job_key.GetVariable( 'queue' )
|
||||
|
||||
queue = list( queue )
|
||||
|
||||
queue.extend( image_infos_i_can_add )
|
||||
|
||||
self._job_key.SetVariable( 'queue', queue )
|
||||
|
||||
|
||||
except HydrusExceptions.NotFoundException: raise Exception( 'Thread 404' )
|
||||
|
||||
last_thread_check = HC.GetNow()
|
||||
|
||||
else: self._job_key.SetVariable( 'status', 'rechecking thread ' + HC.ConvertTimestampToPrettyPending( next_thread_check ) )
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
self._job_key.SetVariable( 'status', HC.u( e ) )
|
||||
|
||||
HC.ShowException( e )
|
||||
|
||||
time.sleep( 2 )
|
||||
|
||||
finally: self._job_key.Finish()
|
||||
|
||||
|
||||
def THREADDownloadURL( message, url, url_string ):
|
||||
|
||||
try:
|
||||
|
|
|
@ -98,14 +98,18 @@ def ParseURL( url ):
|
|||
|
||||
parse_result = urlparse.urlparse( url )
|
||||
|
||||
( scheme, host, port ) = ( parse_result.scheme, parse_result.hostname, parse_result.port )
|
||||
scheme = parse_result.scheme
|
||||
hostname = parse_result.hostname
|
||||
port = parse_result.port
|
||||
|
||||
parse_result = urlparse.urlparse( url )
|
||||
|
||||
location = ( parse_result.scheme, parse_result.hostname, parse_result.port )
|
||||
if hostname is None: location = None
|
||||
else: location = ( scheme, hostname, port )
|
||||
|
||||
path = parse_result.path
|
||||
|
||||
# this happens when parsing 'index.html' rather than 'hostname/index.html' or '/index.html'
|
||||
if not path.startswith( '/' ): path = '/' + path
|
||||
|
||||
query = parse_result.query
|
||||
|
||||
except: raise Exception( 'Could not parse that URL' )
|
||||
|
@ -143,6 +147,8 @@ class HTTPConnectionManager():
|
|||
|
||||
( new_location, new_path, new_query ) = ParseURL( new_url )
|
||||
|
||||
if new_location is None: new_location = location
|
||||
|
||||
if new_query != '': new_path_and_query = new_path + '?' + new_query
|
||||
else: new_path_and_query = new_path
|
||||
|
||||
|
|
Loading…
Reference in New Issue