Version 253
This commit is contained in:
parent
d29afc70bd
commit
f700ad6dc2
|
@ -8,6 +8,33 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 253</h3></li>
|
||||
<ul>
|
||||
<li>created a new object to hold tag and rating merging and 'worse file' deletion options</li>
|
||||
<li>wrote a dialog to edit this new object</li>
|
||||
<li>established some simple defaults for this object for different duplicate status actions</li>
|
||||
<li>the cog icon on the duplicates filter now lets you edit these defaults</li>
|
||||
<li>the 'custom' duplicate filter action now works--first by asking you what status you want to set, and then by throwing up the new merge options dialog to tune it to whatever you like</li>
|
||||
<li>wrote comprehensive unit tests for the new object</li>
|
||||
<li>fixed the super slow dupe filter launch time problem</li>
|
||||
<li>added a 'known urls' submenu to thumbnail and browser canvas right-click menu that lists all known urls for a file with an option to launch or copy the url</li>
|
||||
<li>added known urls' hosts to the top-right canvas details background, just below where known file repos are listed</li>
|
||||
<li>added the same known urls' hosts as clickable hyperlinks to the ratings hover window that pops over that top-right area</li>
|
||||
<li>added 'delete from deleted files' action to the local tags's service-wide update panel. it will limit the deletion to mappings that are currently on files that have been previously completely physically deleted from the program</li>
|
||||
<li>fixed namespace filtering on service-wide update panel</li>
|
||||
<li>the hentai foundry downloader broke, so the update code will pause all hf subs. the solution is not trivial (it is part of the downloader overhaul), but I will try to fix it soon</li>
|
||||
<li>debuted a new question-mark help button to better explain .txt tag importing on the manual import tagging dialog and the manage import folders dialog</li>
|
||||
<li>fixed a small potential error due to bad parsing in the 'page of images' downloader</li>
|
||||
<li>fixed a typo bug that stopped the 'delete shortcuts set' action working in the manage shortcuts dialog</li>
|
||||
<li>I may have fixed an issue where the server was sometimes not shutting cleanly with a keyboardinterrupt</li>
|
||||
<li>fixed the media embed button not reliably updating its thumbnail</li>
|
||||
<li>fixed an issue where a dummy animation bar was displaying on embed buttons that showed static images that included transparency</li>
|
||||
<li>the serious db missing tag and hash states will now not throw an error but will inform/spam the user (and hence not prohibit a boot)</li>
|
||||
<li>attempting to open a second manage tags frame from the media viewer will now instead put the focus on the first (previously, multiple manage tags frames could be made)</li>
|
||||
<li>misc db code cleanup that should result in faster result building in certain situations</li>
|
||||
<li>misc improvements</li>
|
||||
<li>misc layout fixes</li>
|
||||
</ul>
|
||||
<li><h3>version 252</h3></li>
|
||||
<ul>
|
||||
<li>the duplicate filter now processes pairs in batches and hence supports 'back' actions to revisit decisions. you will be prompted every fifty or so pairs to commit and checkpoint your progress</li>
|
||||
|
|
|
@ -245,6 +245,8 @@ SHORTCUT_MOUSE_RIGHT = 1
|
|||
SHORTCUT_MOUSE_MIDDLE = 2
|
||||
SHORTCUT_MOUSE_SCROLL_UP = 3
|
||||
SHORTCUT_MOUSE_SCROLL_DOWN = 4
|
||||
SHORTCUT_MOUSE_SCROLL_LEFT = 5
|
||||
SHORTCUT_MOUSE_SCROLL_RIGHT = 6
|
||||
|
||||
shortcut_mouse_string_lookup = {}
|
||||
|
||||
|
@ -253,6 +255,8 @@ shortcut_mouse_string_lookup[ SHORTCUT_MOUSE_RIGHT ] = 'right-click'
|
|||
shortcut_mouse_string_lookup[ SHORTCUT_MOUSE_MIDDLE ] = 'middle-click'
|
||||
shortcut_mouse_string_lookup[ SHORTCUT_MOUSE_SCROLL_UP ] = 'scroll up'
|
||||
shortcut_mouse_string_lookup[ SHORTCUT_MOUSE_SCROLL_DOWN ] = 'scroll down'
|
||||
shortcut_mouse_string_lookup[ SHORTCUT_MOUSE_SCROLL_LEFT ] = 'scroll left'
|
||||
shortcut_mouse_string_lookup[ SHORTCUT_MOUSE_SCROLL_RIGHT ] = 'scroll right'
|
||||
|
||||
SHORTCUT_TYPE_KEYBOARD = 0
|
||||
SHORTCUT_TYPE_MOUSE = 1
|
||||
|
@ -499,6 +503,7 @@ class GlobalBMPs( object ):
|
|||
|
||||
GlobalBMPs.cog = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'cog.png' ) )
|
||||
GlobalBMPs.keyboard = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'keyboard.png' ) )
|
||||
GlobalBMPs.help = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'help.png' ) )
|
||||
|
||||
GlobalBMPs.check = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'check.png' ) )
|
||||
GlobalBMPs.pause = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'pause.png' ) )
|
||||
|
|
|
@ -1073,6 +1073,11 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
pairs_of_hash_ids.append( ( smaller_hash_id, larger_hash_id ) )
|
||||
|
||||
if len( pairs_of_hash_ids ) >= MAX_BATCH_SIZE:
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
hash_ids_to_hashes = self._GetHashIdsToHashes( seen_hash_ids )
|
||||
|
@ -1335,6 +1340,8 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
with HydrusDB.TemporaryIntegerTable( self._c, rebalance_phash_ids, 'phash_id' ) as temp_table_name:
|
||||
|
||||
# can't turn this into selectfromlist due to the order clause. we need to do this all at once
|
||||
|
||||
( biggest_phash_id, ) = self._c.execute( 'SELECT phash_id FROM shape_vptree NATURAL JOIN ' + temp_table_name + ' ORDER BY inner_population + outer_population DESC;' ).fetchone()
|
||||
|
||||
|
||||
|
@ -3386,7 +3393,9 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if result is None:
|
||||
|
||||
raise HydrusExceptions.DataMissing( 'File hash error in database' )
|
||||
HydrusData.ShowText( 'Database hash error: hash_id ' + str( hash_id ) + ' was missing! This is a serious error that likely needs a repository reset to fix! Think about contacting hydrus dev!' )
|
||||
|
||||
return 'aaaaaaaaaaaaaaaa'.decode( 'hex' ) + os.urandom( 16 )
|
||||
|
||||
|
||||
( hash, ) = result
|
||||
|
@ -3394,7 +3403,10 @@ class DB( HydrusDB.HydrusDB ):
|
|||
return hash
|
||||
|
||||
|
||||
def _GetHashes( self, hash_ids ): return [ hash for ( hash, ) in self._c.execute( 'SELECT hash FROM hashes WHERE hash_id IN ' + HydrusData.SplayListForDB( hash_ids ) + ';' ) ]
|
||||
def _GetHashes( self, hash_ids ):
|
||||
|
||||
return [ hash for ( hash, ) in self._c.execute( 'SELECT hash FROM hashes WHERE hash_id IN ' + HydrusData.SplayListForDB( hash_ids ) + ';' ) ]
|
||||
|
||||
|
||||
def _GetHashId( self, hash ):
|
||||
|
||||
|
@ -3406,7 +3418,10 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
hash_id = self._c.lastrowid
|
||||
|
||||
else: ( hash_id, ) = result
|
||||
else:
|
||||
|
||||
( hash_id, ) = result
|
||||
|
||||
|
||||
return hash_id
|
||||
|
||||
|
@ -4431,15 +4446,6 @@ class DB( HydrusDB.HydrusDB ):
|
|||
return value
|
||||
|
||||
|
||||
def _GetKnownURLs( self, hash ):
|
||||
|
||||
hash_id = self._GetHashId( hash )
|
||||
|
||||
urls = [ url for ( url, ) in self._c.execute( 'SELECT url FROM urls WHERE hash_id = ?;', ( hash_id, ) ) ]
|
||||
|
||||
return urls
|
||||
|
||||
|
||||
def _GetMD5Status( self, md5 ):
|
||||
|
||||
result = self._c.execute( 'SELECT hash_id FROM local_hashes WHERE md5 = ?;', ( sqlite3.Binary( md5 ), ) ).fetchone()
|
||||
|
@ -4460,47 +4466,44 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
# get first detailed results
|
||||
|
||||
with HydrusDB.TemporaryIntegerTable( self._c, hash_ids, 'hash_id' ) as temp_table_name:
|
||||
hash_ids_to_hashes = self._GetHashIdsToHashes( hash_ids )
|
||||
|
||||
hash_ids_to_info = { hash_id : ( size, mime, width, height, duration, num_frames, num_words ) for ( hash_id, size, mime, width, height, duration, num_frames, num_words ) in self._SelectFromList( 'SELECT * FROM files_info WHERE hash_id IN %s;', hash_ids ) }
|
||||
|
||||
hash_ids_to_current_file_service_ids_and_timestamps = HydrusData.BuildKeyToListDict( ( ( hash_id, ( service_id, timestamp ) ) for ( hash_id, service_id, timestamp ) in self._SelectFromList( 'SELECT hash_id, service_id, timestamp FROM current_files WHERE hash_id IN %s;', hash_ids ) ) )
|
||||
|
||||
hash_ids_to_deleted_file_service_ids = HydrusData.BuildKeyToListDict( self._SelectFromList( 'SELECT hash_id, service_id FROM deleted_files WHERE hash_id IN %s;', hash_ids ) )
|
||||
|
||||
hash_ids_to_pending_file_service_ids = HydrusData.BuildKeyToListDict( self._SelectFromList( 'SELECT hash_id, service_id FROM file_transfers WHERE hash_id IN %s;', hash_ids ) )
|
||||
|
||||
hash_ids_to_petitioned_file_service_ids = HydrusData.BuildKeyToListDict( self._SelectFromList( 'SELECT hash_id, service_id FROM file_petitions WHERE hash_id IN %s;', hash_ids ) )
|
||||
|
||||
hash_ids_to_urls = HydrusData.BuildKeyToListDict( self._SelectFromList( 'SELECT hash_id, url FROM urls WHERE hash_id IN %s;', hash_ids ) )
|
||||
|
||||
hash_ids_to_service_ids_and_filenames = HydrusData.BuildKeyToListDict( ( ( hash_id, ( service_id, filename ) ) for ( hash_id, service_id, filename ) in self._SelectFromList( 'SELECT hash_id, service_id, filename FROM service_filenames WHERE hash_id IN %s;', hash_ids ) ) )
|
||||
|
||||
hash_ids_to_local_ratings = HydrusData.BuildKeyToListDict( ( ( hash_id, ( service_id, rating ) ) for ( service_id, hash_id, rating ) in self._SelectFromList( 'SELECT service_id, hash_id, rating FROM local_ratings WHERE hash_id IN %s;', hash_ids ) ) )
|
||||
|
||||
tag_data = []
|
||||
|
||||
tag_service_ids = self._GetServiceIds( HC.TAG_SERVICES )
|
||||
|
||||
for tag_service_id in tag_service_ids:
|
||||
|
||||
hash_ids_to_info = { hash_id : ( size, mime, width, height, duration, num_frames, num_words ) for ( hash_id, size, mime, width, height, duration, num_frames, num_words ) in self._c.execute( 'SELECT * FROM files_info NATURAL JOIN ' + temp_table_name + ';' ) }
|
||||
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( tag_service_id )
|
||||
|
||||
hash_ids_to_hashes = self._GetHashIdsToHashes( hash_ids )
|
||||
|
||||
hash_ids_to_current_file_service_ids_and_timestamps = HydrusData.BuildKeyToListDict( ( ( hash_id, ( service_id, timestamp ) ) for ( hash_id, service_id, timestamp ) in self._c.execute( 'SELECT hash_id, service_id, timestamp FROM current_files NATURAL JOIN ' + temp_table_name + ';' ) ) )
|
||||
|
||||
hash_ids_to_deleted_file_service_ids = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, service_id FROM deleted_files NATURAL JOIN ' + temp_table_name + ';' ) )
|
||||
|
||||
hash_ids_to_pending_file_service_ids = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, service_id FROM file_transfers NATURAL JOIN ' + temp_table_name + ';' ) )
|
||||
|
||||
hash_ids_to_petitioned_file_service_ids = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, service_id FROM file_petitions NATURAL JOIN ' + temp_table_name + ';' ) )
|
||||
|
||||
hash_ids_to_urls = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, url FROM urls NATURAL JOIN ' + temp_table_name + ';' ) )
|
||||
|
||||
hash_ids_to_service_ids_and_filenames = HydrusData.BuildKeyToListDict( ( ( hash_id, ( service_id, filename ) ) for ( hash_id, service_id, filename ) in self._c.execute( 'SELECT hash_id, service_id, filename FROM service_filenames NATURAL JOIN ' + temp_table_name + ';' ) ) )
|
||||
|
||||
hash_ids_to_local_ratings = HydrusData.BuildKeyToListDict( ( ( hash_id, ( service_id, rating ) ) for ( service_id, hash_id, rating ) in self._c.execute( 'SELECT service_id, hash_id, rating FROM local_ratings NATURAL JOIN ' + temp_table_name + ';' ) ) )
|
||||
|
||||
tag_data = []
|
||||
|
||||
tag_service_ids = self._GetServiceIds( HC.TAG_SERVICES )
|
||||
|
||||
for tag_service_id in tag_service_ids:
|
||||
|
||||
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( tag_service_id )
|
||||
|
||||
tag_data.extend( ( hash_id, ( tag_service_id, HC.CONTENT_STATUS_CURRENT, tag_id ) ) for ( hash_id, tag_id ) in self._c.execute( 'SELECT hash_id, tag_id FROM ' + current_mappings_table_name + ' NATURAL JOIN ' + temp_table_name + ';' ) )
|
||||
tag_data.extend( ( hash_id, ( tag_service_id, HC.CONTENT_STATUS_DELETED, tag_id ) ) for ( hash_id, tag_id ) in self._c.execute( 'SELECT hash_id, tag_id FROM ' + deleted_mappings_table_name + ' NATURAL JOIN ' + temp_table_name + ';' ) )
|
||||
tag_data.extend( ( hash_id, ( tag_service_id, HC.CONTENT_STATUS_PENDING, tag_id ) ) for ( hash_id, tag_id ) in self._c.execute( 'SELECT hash_id, tag_id FROM ' + pending_mappings_table_name + ' NATURAL JOIN ' + temp_table_name + ';' ) )
|
||||
tag_data.extend( ( hash_id, ( tag_service_id, HC.CONTENT_STATUS_PETITIONED, tag_id ) ) for ( hash_id, tag_id ) in self._c.execute( 'SELECT hash_id, tag_id FROM ' + petitioned_mappings_table_name + ' NATURAL JOIN ' + temp_table_name + ';' ) )
|
||||
|
||||
|
||||
seen_tag_ids = { tag_id for ( hash_id, ( tag_service_id, status, tag_id ) ) in tag_data }
|
||||
|
||||
hash_ids_to_raw_tag_data = HydrusData.BuildKeyToListDict( tag_data )
|
||||
|
||||
tag_ids_to_tags = self._GetTagIdsToTags( seen_tag_ids )
|
||||
tag_data.extend( ( hash_id, ( tag_service_id, HC.CONTENT_STATUS_CURRENT, tag_id ) ) for ( hash_id, tag_id ) in self._SelectFromList( 'SELECT hash_id, tag_id FROM ' + current_mappings_table_name + ' WHERE hash_id IN %s;', hash_ids ) )
|
||||
tag_data.extend( ( hash_id, ( tag_service_id, HC.CONTENT_STATUS_DELETED, tag_id ) ) for ( hash_id, tag_id ) in self._SelectFromList( 'SELECT hash_id, tag_id FROM ' + deleted_mappings_table_name + ' WHERE hash_id IN %s;', hash_ids ) )
|
||||
tag_data.extend( ( hash_id, ( tag_service_id, HC.CONTENT_STATUS_PENDING, tag_id ) ) for ( hash_id, tag_id ) in self._SelectFromList( 'SELECT hash_id, tag_id FROM ' + pending_mappings_table_name + ' WHERE hash_id IN %s;', hash_ids ) )
|
||||
tag_data.extend( ( hash_id, ( tag_service_id, HC.CONTENT_STATUS_PETITIONED, tag_id ) ) for ( hash_id, tag_id ) in self._SelectFromList( 'SELECT hash_id, tag_id FROM ' + petitioned_mappings_table_name + ' WHERE hash_id IN %s;', hash_ids ) )
|
||||
|
||||
|
||||
seen_tag_ids = { tag_id for ( hash_id, ( tag_service_id, status, tag_id ) ) in tag_data }
|
||||
|
||||
hash_ids_to_raw_tag_data = HydrusData.BuildKeyToListDict( tag_data )
|
||||
|
||||
tag_ids_to_tags = self._GetTagIdsToTags( seen_tag_ids )
|
||||
|
||||
# build it
|
||||
|
||||
service_ids_to_service_keys = { service_id : service_key for ( service_id, service_key ) in self._c.execute( 'SELECT service_id, service_key FROM services;' ) }
|
||||
|
@ -5330,7 +5333,9 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if result is None:
|
||||
|
||||
raise HydrusExceptions.DataMissing( 'Tag error in database' )
|
||||
HydrusData.ShowText( 'Database tag error: tag_id ' + str( tag_id ) + ' was missing! This is a serious error that likely needs a tag repository reset to fix! Think about contacting hydrus dev!' )
|
||||
|
||||
return 'invalid namespace:invalid tag'
|
||||
|
||||
|
||||
( namespace, subtag ) = result
|
||||
|
@ -5348,7 +5353,9 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if len( results ) != len( tag_ids ):
|
||||
|
||||
raise HydrusExceptions.DataMissing( 'Tag error in database' )
|
||||
HydrusData.ShowText( 'Database tag error: some tag_ids were missing! This is a serious error that likely needs a tag repository reset to fix! Think about contacting hydrus dev!' )
|
||||
|
||||
return [ 'invalid namespace:invalid tag ' + str( i ) for i in range( len( tag_ids ) ) ]
|
||||
|
||||
|
||||
tags = [ HydrusTags.CombineTag( namespace, subtag ) for ( namespace, subtag ) in results ]
|
||||
|
@ -6595,12 +6602,14 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
( sub_action, sub_row ) = row
|
||||
|
||||
if sub_action in ( 'copy', 'delete', 'delete_deleted' ):
|
||||
if sub_action in ( 'copy', 'delete', 'delete_deleted', 'delete_for_deleted_files' ):
|
||||
|
||||
self._c.execute( 'CREATE TEMPORARY TABLE temp_operation ( job_id INTEGER PRIMARY KEY AUTOINCREMENT, tag_id INTEGER, hash_id INTEGER );' )
|
||||
|
||||
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( service_id )
|
||||
|
||||
predicates = []
|
||||
|
||||
if sub_action == 'copy':
|
||||
|
||||
( tag, hashes, service_key_target ) = sub_row
|
||||
|
@ -6619,10 +6628,16 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
source_table_name = deleted_mappings_table_name
|
||||
|
||||
elif sub_action == 'delete_for_deleted_files':
|
||||
|
||||
( tag, hashes ) = sub_row
|
||||
|
||||
source_table_name = current_mappings_table_name + ' NATURAL JOIN deleted_files'
|
||||
|
||||
predicates.append( 'deleted_files.service_id = ' + str( self._combined_local_file_service_id ) )
|
||||
|
||||
|
||||
predicates = []
|
||||
|
||||
do_tags_join = False
|
||||
do_namespace_join = False
|
||||
|
||||
if tag is not None:
|
||||
|
||||
|
@ -6636,7 +6651,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
elif tag_type == 'namespace':
|
||||
|
||||
do_tags_join = True
|
||||
do_namespace_join = True
|
||||
|
||||
namespace = tag
|
||||
|
||||
|
@ -6646,21 +6661,21 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
elif tag_type == 'namespaced':
|
||||
|
||||
do_tags_join = True
|
||||
do_namespace_join = True
|
||||
|
||||
predicates.append( 'namespace_id != ' + str( self._null_namespace_id ) )
|
||||
|
||||
elif tag_type == 'unnamespaced':
|
||||
|
||||
do_tags_join = True
|
||||
do_namespace_join = True
|
||||
|
||||
predicates.append( 'namespace_id = ' + str( self._null_namespace_id ) )
|
||||
|
||||
|
||||
|
||||
if do_tags_join:
|
||||
if do_namespace_join:
|
||||
|
||||
source_table_name = source_table_name + ' NATURAL JOIN tags'
|
||||
source_table_name = source_table_name + ' NATURAL JOIN namespaces'
|
||||
|
||||
|
||||
if hashes is not None:
|
||||
|
@ -6710,7 +6725,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
self._UpdateMappings( service_id_target, **kwargs )
|
||||
|
||||
elif sub_action == 'delete':
|
||||
elif sub_action in ( 'delete', 'delete_for_deleted_files' ):
|
||||
|
||||
self._UpdateMappings( service_id, deleted_mappings_ids = advanced_mappings_ids )
|
||||
|
||||
|
@ -6875,7 +6890,6 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
notify_new_parents = True
|
||||
|
||||
|
||||
elif data_type == HC.CONTENT_TYPE_TAG_SIBLINGS:
|
||||
|
||||
if action in ( HC.CONTENT_UPDATE_ADD, HC.CONTENT_UPDATE_DELETE ):
|
||||
|
@ -6965,6 +6979,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
notify_new_siblings = True
|
||||
|
||||
|
||||
elif service_type in HC.RATINGS_SERVICES:
|
||||
|
||||
if action == HC.CONTENT_UPDATE_ADD:
|
||||
|
@ -7267,7 +7282,6 @@ class DB( HydrusDB.HydrusDB ):
|
|||
elif action == 'hydrus_sessions': result = self._GetHydrusSessions( *args, **kwargs )
|
||||
elif action == 'imageboards': result = self._GetYAMLDump( YAML_DUMP_ID_IMAGEBOARD, *args, **kwargs )
|
||||
elif action == 'is_an_orphan': result = self._IsAnOrphan( *args, **kwargs )
|
||||
elif action == 'known_urls': result = self._GetKnownURLs( *args, **kwargs )
|
||||
elif action == 'load_into_disk_cache': result = self._LoadIntoDiskCache( *args, **kwargs )
|
||||
elif action == 'local_booru_share_keys': result = self._GetYAMLDumpNames( YAML_DUMP_ID_LOCAL_BOORU )
|
||||
elif action == 'local_booru_share': result = self._GetYAMLDump( YAML_DUMP_ID_LOCAL_BOORU, *args, **kwargs )
|
||||
|
@ -8744,6 +8758,44 @@ class DB( HydrusDB.HydrusDB ):
|
|||
self._SetJSONDump( media_viewer )
|
||||
|
||||
|
||||
if version == 252:
|
||||
|
||||
try:
|
||||
|
||||
do_the_message = False
|
||||
|
||||
subscriptions = self._GetJSONDumpNamed( HydrusSerialisable.SERIALISABLE_TYPE_SUBSCRIPTION )
|
||||
|
||||
for subscription in subscriptions:
|
||||
|
||||
g_i = subscription._gallery_identifier
|
||||
|
||||
if g_i.GetSiteType() in ( HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST, HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST_PICTURES, HC.SITE_TYPE_HENTAI_FOUNDRY_ARTIST_SCRAPS, HC.SITE_TYPE_HENTAI_FOUNDRY_TAGS ):
|
||||
|
||||
subscription._paused = True
|
||||
|
||||
self._SetJSONDump( subscription )
|
||||
|
||||
do_the_message = True
|
||||
|
||||
|
||||
|
||||
if do_the_message:
|
||||
|
||||
message = 'The Hentai Foundry downloader broke around version 243, so all of your Hentai Foundry subscriptions have been paused!'
|
||||
|
||||
self.pub_initial_message( message )
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.Print( 'While attempting to pause all hentai foundry subs, I had this problem:' )
|
||||
HydrusData.PrintException( e )
|
||||
|
||||
|
||||
self._c.execute( 'UPDATE duplicate_pairs SET duplicate_type = ? WHERE duplicate_type != ?;', ( HC.DUPLICATE_UNKNOWN, HC.DUPLICATE_UNKNOWN ) )
|
||||
|
||||
|
||||
self._controller.pub( 'splash_set_title_text', 'updated db to v' + str( version + 1 ) )
|
||||
|
||||
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
|
||||
|
|
|
@ -684,6 +684,15 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
#
|
||||
|
||||
self._dictionary[ 'duplicate_action_options' ] = HydrusSerialisable.SerialisableDictionary()
|
||||
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_BETTER ] = DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ) ], True )
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_SAME_FILE ] = DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ) ], False )
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_ALTERNATE ] = DuplicateActionOptions( [], False )
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_NOT_DUPLICATE ] = DuplicateActionOptions( [], False )
|
||||
|
||||
#
|
||||
|
||||
self._dictionary[ 'integers' ] = {}
|
||||
|
||||
self._dictionary[ 'integers' ][ 'video_buffer_size_mb' ] = 96
|
||||
|
@ -1068,6 +1077,14 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
def GetDuplicateActionOptions( self, duplicate_status ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._dictionary[ 'duplicate_action_options' ][ duplicate_status ]
|
||||
|
||||
|
||||
|
||||
def GetFrameLocation( self, frame_key ):
|
||||
|
||||
with self._lock:
|
||||
|
@ -1249,6 +1266,14 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
def SetDuplicateActionOptions( self, duplicate_status, duplicate_action_options ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._dictionary[ 'duplicate_action_options' ][ duplicate_status ] = duplicate_action_options
|
||||
|
||||
|
||||
|
||||
def SetFrameLocation( self, frame_key, remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ):
|
||||
|
||||
with self._lock:
|
||||
|
@ -1378,6 +1403,216 @@ class Credentials( HydrusData.HydrusYAMLBase ):
|
|||
|
||||
def SetAccessKey( self, access_key ): self._access_key = access_key
|
||||
|
||||
class DuplicateActionOptions( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_DUPLICATE_ACTION_OPTIONS
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, service_actions = None, delete_second_file = None ):
|
||||
|
||||
if service_actions is None:
|
||||
|
||||
service_actions = []
|
||||
|
||||
|
||||
if delete_second_file is None:
|
||||
|
||||
delete_second_file = False
|
||||
|
||||
|
||||
HydrusSerialisable.SerialisableBase.__init__( self )
|
||||
|
||||
self._service_actions = service_actions
|
||||
self._delete_second_file = delete_second_file
|
||||
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
||||
serialisable_service_actions = [ ( service_key.encode( 'hex' ), action ) for ( service_key, action ) in self._service_actions ]
|
||||
|
||||
return ( serialisable_service_actions, self._delete_second_file )
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
( serialisable_service_actions, self._delete_second_file ) = serialisable_info
|
||||
|
||||
self._service_actions = [ ( serialisable_service_key.decode( 'hex' ), action ) for ( serialisable_service_key, action ) in serialisable_service_actions ]
|
||||
|
||||
|
||||
def SetTuple( self, service_actions, delete_second_file ):
|
||||
|
||||
self._service_actions = service_actions
|
||||
self._delete_second_file = delete_second_file
|
||||
|
||||
|
||||
def ToTuple( self ):
|
||||
|
||||
return ( self._service_actions, self._delete_second_file )
|
||||
|
||||
|
||||
def ProcessPairIntoContentUpdates( self, first_media, second_media ):
|
||||
|
||||
content_service_keys_to_content_updates = {}
|
||||
file_service_keys_to_content_updates = {}
|
||||
|
||||
first_hashes = first_media.GetHashes()
|
||||
second_hashes = second_media.GetHashes()
|
||||
|
||||
services_manager = HydrusGlobals.client_controller.GetServicesManager()
|
||||
|
||||
for ( service_key, action ) in self._service_actions:
|
||||
|
||||
content_updates = []
|
||||
|
||||
try:
|
||||
|
||||
service = services_manager.GetService( service_key )
|
||||
|
||||
except HydrusExceptions.DataMissing:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
service_type = service.GetServiceType()
|
||||
|
||||
if service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
|
||||
|
||||
first_current_value = first_media.GetRatingsManager().GetRating( service_key )
|
||||
second_current_value = second_media.GetRatingsManager().GetRating( service_key )
|
||||
|
||||
if action == HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE:
|
||||
|
||||
if first_current_value == second_current_value:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
if first_current_value is None and second_current_value is not None:
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( second_current_value, first_hashes ) ) )
|
||||
|
||||
elif first_current_value is not None and second_current_value is None:
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( first_current_value, second_hashes ) ) )
|
||||
|
||||
|
||||
elif action == HC.CONTENT_MERGE_ACTION_COPY:
|
||||
|
||||
if first_current_value == second_current_value:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
if first_current_value is None and second_current_value is not None:
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( second_current_value, first_hashes ) ) )
|
||||
|
||||
|
||||
elif action == HC.CONTENT_MERGE_ACTION_MOVE:
|
||||
|
||||
if second_current_value is not None:
|
||||
|
||||
if first_current_value is None:
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( second_current_value, first_hashes ) ) )
|
||||
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( None, second_hashes ) ) )
|
||||
|
||||
|
||||
|
||||
elif service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ):
|
||||
|
||||
if service_type == HC.LOCAL_TAG:
|
||||
|
||||
add_content_action = HC.CONTENT_UPDATE_ADD
|
||||
|
||||
elif service_type == HC.TAG_REPOSITORY:
|
||||
|
||||
add_content_action = HC.CONTENT_UPDATE_PEND
|
||||
|
||||
|
||||
first_current_tags = first_media.GetTagsManager().GetCurrent( service_key )
|
||||
second_current_tags = second_media.GetTagsManager().GetCurrent( service_key )
|
||||
|
||||
if action == HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE:
|
||||
|
||||
first_needs = second_current_tags.difference( first_current_tags )
|
||||
second_needs = first_current_tags.difference( second_current_tags )
|
||||
|
||||
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, add_content_action, ( tag, first_hashes ) ) for tag in first_needs ) )
|
||||
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, add_content_action, ( tag, second_hashes ) ) for tag in second_needs ) )
|
||||
|
||||
elif action == HC.CONTENT_MERGE_ACTION_COPY:
|
||||
|
||||
first_needs = second_current_tags.difference( first_current_tags )
|
||||
|
||||
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, add_content_action, ( tag, first_hashes ) ) for tag in first_needs ) )
|
||||
|
||||
elif service_type == HC.LOCAL_TAG and action == HC.CONTENT_MERGE_ACTION_MOVE:
|
||||
|
||||
first_needs = second_current_tags.difference( first_current_tags )
|
||||
|
||||
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, add_content_action, ( tag, first_hashes ) ) for tag in first_needs ) )
|
||||
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE, ( tag, second_hashes ) ) for tag in second_current_tags ) )
|
||||
|
||||
|
||||
|
||||
if len( content_updates ) > 0:
|
||||
|
||||
content_service_keys_to_content_updates[ service_key ] = content_updates
|
||||
|
||||
|
||||
|
||||
if self._delete_second_file:
|
||||
|
||||
current_locations = second_media.GetLocationsManager().GetCurrent()
|
||||
|
||||
if CC.LOCAL_FILE_SERVICE_KEY in current_locations:
|
||||
|
||||
deletee_service_key = CC.LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
elif CC.TRASH_SERVICE_KEY in current_locations:
|
||||
|
||||
deletee_service_key = CC.TRASH_SERVICE_KEY
|
||||
|
||||
else:
|
||||
|
||||
deletee_service_key = None
|
||||
|
||||
|
||||
if deletee_service_key is not None:
|
||||
|
||||
if deletee_service_key not in file_service_keys_to_content_updates:
|
||||
|
||||
file_service_keys_to_content_updates[ deletee_service_key ] = []
|
||||
|
||||
|
||||
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, second_hashes )
|
||||
|
||||
file_service_keys_to_content_updates[ deletee_service_key ].append( content_update )
|
||||
|
||||
|
||||
|
||||
|
||||
list_of_service_keys_to_content_updates = []
|
||||
|
||||
if len( content_service_keys_to_content_updates ) > 0:
|
||||
|
||||
list_of_service_keys_to_content_updates.append( content_service_keys_to_content_updates )
|
||||
|
||||
|
||||
if len( file_service_keys_to_content_updates ) > 0:
|
||||
|
||||
list_of_service_keys_to_content_updates.append( file_service_keys_to_content_updates )
|
||||
|
||||
|
||||
return list_of_service_keys_to_content_updates
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_DUPLICATE_ACTION_OPTIONS ] = DuplicateActionOptions
|
||||
|
||||
class Imageboard( HydrusData.HydrusYAMLBase ):
|
||||
|
||||
yaml_tag = u'!Imageboard'
|
||||
|
|
|
@ -1311,7 +1311,7 @@ class GalleryHentaiFoundry( Gallery ):
|
|||
|
||||
# can't parse this easily normally because HF is a pain with the preview->click to see full size business.
|
||||
# find http://pictures.hentai-foundry.com//
|
||||
# then extend it to http://pictures.hentai-foundry.com//k/KABOS/172144.jpg
|
||||
# then extend it to http://pictures.hentai-foundry.com//k/KABOS/172144/image.jpg
|
||||
# the .jpg bit is what we really need, but whatever
|
||||
try:
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ import HydrusPaths
|
|||
import HydrusSerialisable
|
||||
import HydrusTags
|
||||
import os
|
||||
import urlparse
|
||||
import webbrowser
|
||||
import wx
|
||||
|
||||
if HC.PLATFORM_WINDOWS: import wx.lib.flashwin
|
||||
|
@ -1162,6 +1164,8 @@ class Canvas( wx.Window ):
|
|||
self._dirty = True
|
||||
self._closing = False
|
||||
|
||||
self._manage_tags_panel = None
|
||||
|
||||
self._service_keys_to_services = {}
|
||||
|
||||
self._current_media = None
|
||||
|
@ -1460,7 +1464,11 @@ class Canvas( wx.Window ):
|
|||
|
||||
def _ManageTags( self ):
|
||||
|
||||
if self._current_media is not None:
|
||||
if self._manage_tags_panel:
|
||||
|
||||
self._manage_tags_panel.SetFocus()
|
||||
|
||||
elif self._current_media is not None:
|
||||
|
||||
# take any focus away from hover window, which will mess up window order when it hides due to the new frame
|
||||
self.SetFocus()
|
||||
|
@ -1474,6 +1482,8 @@ class Canvas( wx.Window ):
|
|||
|
||||
manage_tags.SetPanel( panel )
|
||||
|
||||
self._manage_tags_panel = panel
|
||||
|
||||
|
||||
|
||||
def _OpenExternally( self ):
|
||||
|
@ -2287,6 +2297,31 @@ class CanvasPanel( Canvas ):
|
|||
|
||||
ClientGUIMenus.AppendMenuItem( self, menu, 'open externally', 'Open the file in your OS\'s default program.', self._OpenExternally )
|
||||
|
||||
urls = self._current_media.GetLocationsManager().GetURLs()
|
||||
|
||||
if len( urls ) > 0:
|
||||
|
||||
urls = list( urls )
|
||||
|
||||
urls.sort()
|
||||
|
||||
urls_menu = wx.Menu()
|
||||
|
||||
urls_visit_menu = wx.Menu()
|
||||
urls_copy_menu = wx.Menu()
|
||||
|
||||
for url in urls:
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, urls_visit_menu, url, 'Open this url in your web browser.', webbrowser.open, url )
|
||||
ClientGUIMenus.AppendMenuItem( self, urls_copy_menu, url, 'Copy this url to your clipboard.', HydrusGlobals.client_controller.pub, 'clipboard', 'text', url )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenu( urls_menu, urls_visit_menu, 'open' )
|
||||
ClientGUIMenus.AppendMenu( urls_menu, urls_copy_menu, 'copy' )
|
||||
|
||||
ClientGUIMenus.AppendMenu( menu, urls_menu, 'known urls' )
|
||||
|
||||
|
||||
share_menu = wx.Menu()
|
||||
|
||||
copy_menu = wx.Menu()
|
||||
|
@ -2472,6 +2507,27 @@ class CanvasWithDetails( Canvas ):
|
|||
current_y += text_height + 4
|
||||
|
||||
|
||||
# urls
|
||||
|
||||
urls = self._current_media.GetLocationsManager().GetURLs()
|
||||
|
||||
urls = list( urls )
|
||||
|
||||
urls.sort()
|
||||
|
||||
for url in urls:
|
||||
|
||||
parse = urlparse.urlparse( url )
|
||||
|
||||
url_string = parse.scheme + '://' + parse.hostname
|
||||
|
||||
( text_width, text_height ) = dc.GetTextExtent( url_string )
|
||||
|
||||
dc.DrawText( url_string, client_width - text_width - 3, current_y )
|
||||
|
||||
current_y += text_height + 4
|
||||
|
||||
|
||||
# ratings
|
||||
|
||||
services_manager = HydrusGlobals.client_controller.GetServicesManager()
|
||||
|
@ -2647,9 +2703,19 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
def _CommitProcessed( self ):
|
||||
|
||||
for ( hash_pair, duplicate_status, hash_a, hash_b, merge_options ) in self._processed_pairs:
|
||||
for ( hash_pair, duplicate_status, first_media, second_media, duplicate_action_options ) in self._processed_pairs:
|
||||
|
||||
HydrusGlobals.client_controller.WriteSynchronous( 'duplicate_pair_status', duplicate_status, hash_a, hash_b, merge_options )
|
||||
list_of_service_keys_to_content_updates = duplicate_action_options.ProcessPairIntoContentUpdates( first_media, second_media )
|
||||
|
||||
for service_keys_to_content_updates in list_of_service_keys_to_content_updates:
|
||||
|
||||
HydrusGlobals.client_controller.Write( 'content_updates', service_keys_to_content_updates )
|
||||
|
||||
|
||||
first_hash = first_media.GetHash()
|
||||
second_hash = second_media.GetHash()
|
||||
|
||||
HydrusGlobals.client_controller.WriteSynchronous( 'duplicate_pair_status', duplicate_status, first_hash, second_hash, duplicate_action_options )
|
||||
|
||||
|
||||
self._processed_pairs = []
|
||||
|
@ -2662,16 +2728,35 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
def _DoCustomAction( self ):
|
||||
|
||||
wx.MessageBox( 'This doesn\'t do anything yet!' )
|
||||
duplicate_statuses = [ HC.DUPLICATE_BETTER, HC.DUPLICATE_SAME_FILE, HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_NOT_DUPLICATE ]
|
||||
|
||||
return
|
||||
choice_tuples = [ ( HC.duplicate_status_string_lookup[ duplicate_status ], duplicate_status ) for duplicate_status in duplicate_statuses ]
|
||||
|
||||
# ( duplicate_status, hash_a, hash_b, merge_options ) = panel.getvalue()
|
||||
# HydrusGlobals.client_controller.WriteSynchronous( 'duplicate_pair_status', duplicate_status, hash_a, hash_b, merge_options )
|
||||
|
||||
# launch the dialog to choose exactly what happens
|
||||
# if OK on that:
|
||||
self._ShowNewPair()
|
||||
with ClientGUIDialogs.DialogSelectFromList( self, 'select duplicate_status', choice_tuples ) as dlg_1:
|
||||
|
||||
if dlg_1.ShowModal() == wx.ID_OK:
|
||||
|
||||
duplicate_status = dlg_1.GetChoice()
|
||||
|
||||
new_options = HydrusGlobals.client_controller.GetNewOptions()
|
||||
|
||||
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_status )
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit duplicate merge options' ) as dlg_2:
|
||||
|
||||
panel = ClientGUIScrolledPanelsEdit.EditDuplicateActionOptionsPanel( dlg_2, duplicate_status, duplicate_action_options )
|
||||
|
||||
dlg_2.SetPanel( panel )
|
||||
|
||||
if dlg_2.ShowModal() == wx.ID_OK:
|
||||
|
||||
duplicate_action_options = panel.GetValue()
|
||||
|
||||
self._ProcessPair( duplicate_status, duplicate_action_options )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _GenerateHoverTopFrame( self ):
|
||||
|
@ -2698,20 +2783,13 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
|
||||
|
||||
def _GetMergeOptions( self, duplicate_status ):
|
||||
|
||||
# fetch it from client_options, given a status
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _GoBack( self ):
|
||||
|
||||
if len( self._processed_pairs ) > 0:
|
||||
|
||||
self._unprocessed_pairs.append( self._current_pair )
|
||||
|
||||
( hash_pair, duplicate_status, hash_a, hash_b, merge_options ) = self._processed_pairs.pop()
|
||||
( hash_pair, duplicate_status, first_media, second_media, duplicate_action_options ) = self._processed_pairs.pop()
|
||||
|
||||
self._unprocessed_pairs.append( hash_pair )
|
||||
|
||||
|
@ -2791,16 +2869,18 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
return command_processed
|
||||
|
||||
|
||||
def _ProcessPair( self, duplicate_status ):
|
||||
def _ProcessPair( self, duplicate_status, duplicate_action_options = None ):
|
||||
|
||||
if duplicate_action_options is None:
|
||||
|
||||
new_options = HydrusGlobals.client_controller.GetNewOptions()
|
||||
|
||||
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_status )
|
||||
|
||||
|
||||
other_media = self._media_list.GetNext( self._current_media )
|
||||
|
||||
hash_a = self._current_media.GetHash()
|
||||
hash_b = other_media.GetHash()
|
||||
|
||||
merge_options = self._GetMergeOptions( HC.DUPLICATE_SAME_FILE )
|
||||
|
||||
self._processed_pairs.append( ( self._current_pair, duplicate_status, hash_a, hash_b, merge_options ) )
|
||||
self._processed_pairs.append( ( self._current_pair, duplicate_status, self._current_media, other_media, duplicate_action_options ) )
|
||||
|
||||
self._ShowNewPair()
|
||||
|
||||
|
@ -2821,7 +2901,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
else:
|
||||
|
||||
( hash_pair, duplicate_status, hash_a, hash_b, merge_options ) = self._processed_pairs.pop()
|
||||
( hash_pair, duplicate_status, first_media, second_media, duplicate_action_options ) = self._processed_pairs.pop()
|
||||
|
||||
self._unprocessed_pairs.append( hash_pair )
|
||||
|
||||
|
@ -3942,146 +4022,174 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
|
|||
|
||||
def EventShowMenu( self, event ):
|
||||
|
||||
services = HydrusGlobals.client_controller.GetServicesManager().GetServices()
|
||||
|
||||
local_ratings_services = [ service for service in services if service.GetServiceType() in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) ]
|
||||
|
||||
i_can_post_ratings = len( local_ratings_services ) > 0
|
||||
|
||||
self._last_drag_coordinates = None # to stop successive right-click drag warp bug
|
||||
|
||||
locations_manager = self._current_media.GetLocationsManager()
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
for line in self._current_media.GetPrettyInfoLines():
|
||||
if self._current_media is not None:
|
||||
|
||||
menu.Append( CC.ID_NULL, line )
|
||||
services = HydrusGlobals.client_controller.GetServicesManager().GetServices()
|
||||
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
if self._IsZoomable():
|
||||
local_ratings_services = [ service for service in services if service.GetServiceType() in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) ]
|
||||
|
||||
menu.Append( CC.ID_NULL, 'current zoom: ' + ClientData.ConvertZoomToPercentage( self._current_zoom ) )
|
||||
i_can_post_ratings = len( local_ratings_services ) > 0
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'zoom_in' ), 'zoom in' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'zoom_out' ), 'zoom out' )
|
||||
self._last_drag_coordinates = None # to stop successive right-click drag warp bug
|
||||
|
||||
if self._current_media.GetMime() != HC.APPLICATION_FLASH:
|
||||
locations_manager = self._current_media.GetLocationsManager()
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
for line in self._current_media.GetPrettyInfoLines():
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
( media_width, media_height ) = self._current_media.GetResolution()
|
||||
|
||||
if self._current_zoom == 1.0:
|
||||
|
||||
if media_width > my_width or media_height > my_height:
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'switch_between_100_percent_and_canvas_zoom' ), 'zoom fit' )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'switch_between_100_percent_and_canvas_zoom' ), 'zoom full' )
|
||||
|
||||
menu.Append( CC.ID_NULL, line )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
|
||||
if i_can_post_ratings:
|
||||
if self._IsZoomable():
|
||||
|
||||
menu.Append( CC.ID_NULL, 'current zoom: ' + ClientData.ConvertZoomToPercentage( self._current_zoom ) )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'zoom_in' ), 'zoom in' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'zoom_out' ), 'zoom out' )
|
||||
|
||||
if self._current_media.GetMime() != HC.APPLICATION_FLASH:
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
( media_width, media_height ) = self._current_media.GetResolution()
|
||||
|
||||
if self._current_zoom == 1.0:
|
||||
|
||||
if media_width > my_width or media_height > my_height:
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'switch_between_100_percent_and_canvas_zoom' ), 'zoom fit' )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'switch_between_100_percent_and_canvas_zoom' ), 'zoom full' )
|
||||
|
||||
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
|
||||
manage_menu = wx.Menu()
|
||||
if i_can_post_ratings:
|
||||
|
||||
manage_menu = wx.Menu()
|
||||
|
||||
manage_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'manage_file_tags' ), 'tags' )
|
||||
manage_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'manage_file_ratings' ), 'ratings' )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, 'manage', manage_menu )
|
||||
|
||||
else:
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'manage_file_tags' ), 'manage tags' )
|
||||
|
||||
|
||||
manage_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'manage_file_tags' ), 'tags' )
|
||||
manage_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'manage_file_ratings' ), 'ratings' )
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, 'manage', manage_menu )
|
||||
if self._current_media.HasInbox(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'archive_file' ), '&archive' )
|
||||
if self._current_media.HasArchive(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'inbox_file' ), 'return to &inbox' )
|
||||
|
||||
else:
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'remove_file_from_view' ), '&remove' )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'manage_file_tags' ), 'manage tags' )
|
||||
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'delete_file', CC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
|
||||
|
||||
elif CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'delete_file', CC.TRASH_SERVICE_KEY ), '&delete from trash now' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'undelete' ), '&undelete' )
|
||||
|
||||
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
if self._current_media.HasInbox(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'archive_file' ), '&archive' )
|
||||
if self._current_media.HasArchive(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'inbox_file' ), 'return to &inbox' )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'remove_file_from_view' ), '&remove' )
|
||||
|
||||
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'delete_file', CC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'open_file_in_external_program' ), '&open externally' )
|
||||
|
||||
elif CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
urls = self._current_media.GetLocationsManager().GetURLs()
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'delete_file', CC.TRASH_SERVICE_KEY ), '&delete from trash now' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'undelete' ), '&undelete' )
|
||||
if len( urls ) > 0:
|
||||
|
||||
urls = list( urls )
|
||||
|
||||
urls.sort()
|
||||
|
||||
urls_menu = wx.Menu()
|
||||
|
||||
urls_visit_menu = wx.Menu()
|
||||
urls_copy_menu = wx.Menu()
|
||||
|
||||
for url in urls:
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, urls_visit_menu, url, 'Open this url in your web browser.', webbrowser.open, url )
|
||||
ClientGUIMenus.AppendMenuItem( self, urls_copy_menu, url, 'Copy this url to your clipboard.', HydrusGlobals.client_controller.pub, 'clipboard', 'text', url )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenu( urls_menu, urls_visit_menu, 'open' )
|
||||
ClientGUIMenus.AppendMenu( urls_menu, urls_copy_menu, 'copy' )
|
||||
|
||||
ClientGUIMenus.AppendMenu( menu, urls_menu, 'known urls' )
|
||||
|
||||
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'open_file_in_external_program' ), '&open externally' )
|
||||
|
||||
share_menu = wx.Menu()
|
||||
|
||||
copy_menu = wx.Menu()
|
||||
|
||||
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_files' ), 'file' )
|
||||
|
||||
copy_hash_menu = wx.Menu()
|
||||
|
||||
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'sha256' ) , 'sha256 (hydrus default)' )
|
||||
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'md5' ) , 'md5' )
|
||||
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'sha1' ) , 'sha1' )
|
||||
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'sha512' ) , 'sha512' )
|
||||
|
||||
copy_menu.AppendMenu( CC.ID_NULL, 'hash', copy_hash_menu )
|
||||
|
||||
if self._current_media.GetMime() in HC.IMAGES and self._current_media.GetDuration() is None:
|
||||
share_menu = wx.Menu()
|
||||
|
||||
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_bmp' ), 'image' )
|
||||
copy_menu = wx.Menu()
|
||||
|
||||
|
||||
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_path' ), 'path' )
|
||||
|
||||
share_menu.AppendMenu( CC.ID_NULL, 'copy', copy_menu )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, 'share', share_menu )
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
slideshow = wx.Menu()
|
||||
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 1000 ), '1 second' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 5000 ), '5 seconds' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 10000 ), '10 seconds' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 30000 ), '30 seconds' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 60000 ), '60 seconds' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 80 ), 'william gibson' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow' ), 'custom interval' )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, 'start slideshow', slideshow )
|
||||
|
||||
if self._timer_slideshow.IsRunning():
|
||||
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_files' ), 'file' )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow_pause_play' ), 'stop slideshow' )
|
||||
copy_hash_menu = wx.Menu()
|
||||
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
if self.GetParent().IsFullScreen():
|
||||
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'sha256' ) , 'sha256 (hydrus default)' )
|
||||
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'md5' ) , 'md5' )
|
||||
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'sha1' ) , 'sha1' )
|
||||
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'sha512' ) , 'sha512' )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'switch_between_fullscreen_borderless_and_regular_framed_window' ), 'exit fullscreen' )
|
||||
copy_menu.AppendMenu( CC.ID_NULL, 'hash', copy_hash_menu )
|
||||
|
||||
else:
|
||||
if self._current_media.GetMime() in HC.IMAGES and self._current_media.GetDuration() is None:
|
||||
|
||||
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_bmp' ), 'image' )
|
||||
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'switch_between_fullscreen_borderless_and_regular_framed_window' ), 'go fullscreen' )
|
||||
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_path' ), 'path' )
|
||||
|
||||
share_menu.AppendMenu( CC.ID_NULL, 'copy', copy_menu )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, 'share', share_menu )
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
slideshow = wx.Menu()
|
||||
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 1000 ), '1 second' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 5000 ), '5 seconds' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 10000 ), '10 seconds' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 30000 ), '30 seconds' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 60000 ), '60 seconds' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow', 80 ), 'william gibson' )
|
||||
slideshow.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow' ), 'custom interval' )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, 'start slideshow', slideshow )
|
||||
|
||||
if self._timer_slideshow.IsRunning():
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'slideshow_pause_play' ), 'stop slideshow' )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
if self.GetParent().IsFullScreen():
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'switch_between_fullscreen_borderless_and_regular_framed_window' ), 'exit fullscreen' )
|
||||
|
||||
else:
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'switch_between_fullscreen_borderless_and_regular_framed_window' ), 'go fullscreen' )
|
||||
|
||||
|
||||
HydrusGlobals.client_controller.PopupMenu( self, menu )
|
||||
|
||||
|
||||
HydrusGlobals.client_controller.PopupMenu( self, menu )
|
||||
|
||||
event.Skip()
|
||||
|
||||
|
@ -4256,6 +4364,7 @@ class MediaContainer( wx.Window ):
|
|||
if self._media_window is None:
|
||||
|
||||
self._embed_button.SetSize( ( my_width, my_height ) )
|
||||
self._embed_button.SetPosition( ( 0, 0 ) )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -4492,10 +4601,13 @@ class EmbedButton( wx.Window ):
|
|||
|
||||
if self._thumbnail_bmp is not None:
|
||||
|
||||
# animations will have the animation bar space underneath in this case, so colour it in
|
||||
dc.SetBackground( wx.Brush( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) )
|
||||
|
||||
dc.DrawRectangle( 0, y - ANIMATED_SCANBAR_HEIGHT, x, ANIMATED_SCANBAR_HEIGHT )
|
||||
if ShouldHaveAnimationBar( self._media ):
|
||||
|
||||
# animations will have the animation bar space underneath in this case, so colour it in
|
||||
dc.SetBackground( wx.Brush( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) )
|
||||
|
||||
dc.DrawRectangle( 0, y - ANIMATED_SCANBAR_HEIGHT, x, ANIMATED_SCANBAR_HEIGHT )
|
||||
|
||||
|
||||
( thumb_width, thumb_height ) = self._thumbnail_bmp.GetSize()
|
||||
|
||||
|
@ -4542,7 +4654,12 @@ class EmbedButton( wx.Window ):
|
|||
|
||||
dc.DrawRectangle( 0, 0, x, y )
|
||||
|
||||
self._dirty = False
|
||||
|
||||
def _SetDirty( self ):
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.Refresh()
|
||||
|
||||
|
||||
def EventEraseBackground( self, event ):
|
||||
|
@ -4589,9 +4706,7 @@ class EmbedButton( wx.Window ):
|
|||
|
||||
self._canvas_bmp = wx.EmptyBitmap( my_width, my_height, 24 )
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.Refresh()
|
||||
self._SetDirty()
|
||||
|
||||
|
||||
|
||||
|
@ -4617,6 +4732,8 @@ class EmbedButton( wx.Window ):
|
|||
|
||||
self._thumbnail_bmp = ClientRendering.GenerateHydrusBitmap( thumbnail_path ).GetWxBitmap()
|
||||
|
||||
self._SetDirty()
|
||||
|
||||
else:
|
||||
|
||||
self._thumbnail_bmp = None
|
||||
|
|
|
@ -310,9 +310,9 @@ class BetterRadioBox( wx.RadioBox ):
|
|||
|
||||
class BetterStaticText( wx.StaticText ):
|
||||
|
||||
def __init__( self, parent, label = None ):
|
||||
def __init__( self, parent, label = None, **kwargs ):
|
||||
|
||||
wx.StaticText.__init__( self, parent )
|
||||
wx.StaticText.__init__( self, parent, **kwargs )
|
||||
|
||||
if label is not None:
|
||||
|
||||
|
|
|
@ -2162,7 +2162,6 @@ class DialogPathsToTags( Dialog ):
|
|||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
|
||||
def _GetTags( self, index, path ):
|
||||
|
||||
tags = []
|
||||
|
@ -2522,9 +2521,11 @@ class DialogPathsToTags( Dialog ):
|
|||
self._checkboxes_panel = ClientGUICommon.StaticBox( self, 'misc' )
|
||||
|
||||
self._load_from_txt_files_checkbox = wx.CheckBox( self._checkboxes_panel, label = 'try to load tags from neighbouring .txt files' )
|
||||
self._load_from_txt_files_checkbox.SetToolTipString( 'This looks for a [path].txt file, and will try to load line-separated tags from it. Look at thumbnail->share->export->files\'s txt file checkbox for an example.' )
|
||||
self._load_from_txt_files_checkbox.Bind( wx.EVT_CHECKBOX, self.EventRefresh )
|
||||
|
||||
txt_files_help_button = ClientGUICommon.BetterBitmapButton( self._checkboxes_panel, CC.GlobalBMPs.help, self._ShowTXTHelp )
|
||||
txt_files_help_button.SetToolTipString( 'Show help regarding importing tags from .txt files.' )
|
||||
|
||||
self._filename_namespace = wx.TextCtrl( self._checkboxes_panel )
|
||||
self._filename_namespace.Bind( wx.EVT_TEXT, self.EventRefresh )
|
||||
|
||||
|
@ -2557,6 +2558,11 @@ class DialogPathsToTags( Dialog ):
|
|||
self._single_tags_panel.AddF( self._single_tags, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._single_tags_panel.AddF( self._single_tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
txt_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
txt_hbox.AddF( self._load_from_txt_files_checkbox, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
txt_hbox.AddF( txt_files_help_button, CC.FLAGS_VCENTER )
|
||||
|
||||
filename_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
filename_hbox.AddF( self._filename_checkbox, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
@ -2577,7 +2583,7 @@ class DialogPathsToTags( Dialog ):
|
|||
dir_hbox_3.AddF( self._dir_checkbox_3, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
dir_hbox_3.AddF( self._dir_namespace_3, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self._checkboxes_panel.AddF( self._load_from_txt_files_checkbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._checkboxes_panel.AddF( txt_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
self._checkboxes_panel.AddF( filename_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
self._checkboxes_panel.AddF( dir_hbox_1, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
self._checkboxes_panel.AddF( dir_hbox_2, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
@ -2592,6 +2598,21 @@ class DialogPathsToTags( Dialog ):
|
|||
self.SetSizer( hbox )
|
||||
|
||||
|
||||
def _ShowTXTHelp( self ):
|
||||
|
||||
message = 'If you would like to add custom tags with your files, add a .txt file beside the file like so:'
|
||||
message += os.linesep * 2
|
||||
message += 'my_file.jpg'
|
||||
message += os.linesep
|
||||
message += 'my_file.jpg.txt'
|
||||
message += os.linesep * 2
|
||||
message += 'And include your tags inside the .txt file in a newline-separated list (if you know how to script, generating these files automatically from another source of tags can save a lot of time!).'
|
||||
message += os.linesep * 2
|
||||
message += 'Make sure you preview the results in the table above to be certain everything is parsing correctly. Until you are comfortable with this, you should test it on just one or two files.'
|
||||
|
||||
wx.MessageBox( message )
|
||||
|
||||
|
||||
def EnterTags( self, tags ):
|
||||
|
||||
tag_parents_manager = HydrusGlobals.client_controller.GetManager( 'tag_parents' )
|
||||
|
@ -2675,11 +2696,20 @@ class DialogPathsToTags( Dialog ):
|
|||
|
||||
txt_tags = [ HydrusData.ToUnicode( tag ) for tag in HydrusData.SplitByLinesep( txt_tags_string ) ]
|
||||
|
||||
if True in ( len( txt_tag ) > 1024 for txt_tag in txt_tags ):
|
||||
|
||||
HydrusData.ShowText( 'Tags were too long--I think this was not a regular text file!' )
|
||||
|
||||
raise Exception()
|
||||
|
||||
|
||||
tags.extend( txt_tags )
|
||||
|
||||
except:
|
||||
|
||||
HydrusData.Print( 'Could not parse the tags from ' + txt_path + '!' )
|
||||
HydrusData.ShowText( 'Could not parse the tags from ' + txt_path + '!' )
|
||||
|
||||
tags.append( '___had problem parsing .txt file' )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2543,6 +2543,9 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
|
|||
self._txt_parse_button = wx.Button( self._tag_box, label = 'edit .txt parsing' )
|
||||
self._txt_parse_button.Bind( wx.EVT_BUTTON, self.EventEditTxtParsing )
|
||||
|
||||
txt_files_help_button = ClientGUICommon.BetterBitmapButton( self._tag_box, CC.GlobalBMPs.help, self._ShowTXTHelp )
|
||||
txt_files_help_button.SetToolTipString( 'Show help regarding importing tags from .txt files.' )
|
||||
|
||||
#
|
||||
|
||||
self._ok = wx.Button( self, label = 'ok' )
|
||||
|
@ -2639,9 +2642,14 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
|
|||
|
||||
#
|
||||
|
||||
txt_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
txt_hbox.AddF( self._txt_parse_button, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
txt_hbox.AddF( txt_files_help_button, CC.FLAGS_VCENTER )
|
||||
|
||||
self._tag_box.AddF( self._import_tag_options, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
self._tag_box.AddF( self._txt_parse_st, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
self._tag_box.AddF( self._txt_parse_button, CC.FLAGS_VCENTER )
|
||||
self._tag_box.AddF( txt_hbox, CC.FLAGS_SIZER_VCENTER )
|
||||
|
||||
#
|
||||
|
||||
|
@ -2742,6 +2750,21 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
|
|||
self._txt_parse_st.SetLabelText( text )
|
||||
|
||||
|
||||
def _ShowTXTHelp( self ):
|
||||
|
||||
message = 'If you would like to add custom tags with your files, add a .txt file beside the file like so:'
|
||||
message += os.linesep * 2
|
||||
message += 'my_file.jpg'
|
||||
message += os.linesep
|
||||
message += 'my_file.jpg.txt'
|
||||
message += os.linesep * 2
|
||||
message += 'And include your tags inside the .txt file in a newline-separated list (if you know how to script, generating these files automatically from another source of tags can save a lot of time!).'
|
||||
message += os.linesep * 2
|
||||
message += 'If you are not absolutely comfortable with this, practise it through the manual import process.'
|
||||
|
||||
wx.MessageBox( message )
|
||||
|
||||
|
||||
def EventCheckLocations( self, event ):
|
||||
|
||||
self._CheckLocations()
|
||||
|
|
|
@ -11,6 +11,7 @@ import HydrusConstants as HC
|
|||
import HydrusData
|
||||
import HydrusGlobals
|
||||
import HydrusSerialisable
|
||||
import urlparse
|
||||
import os
|
||||
import wx
|
||||
|
||||
|
@ -402,7 +403,7 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
|
|||
return
|
||||
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'manage shortcuts' ) as dlg:
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'choose shortcuts' ) as dlg:
|
||||
|
||||
choice_tuples = [ ( name, name, name in default_media_viewer_custom_shortcuts ) for name in custom_shortcuts_names ]
|
||||
|
||||
|
@ -499,22 +500,12 @@ class FullscreenHoverFrameTopDuplicatesFilter( FullscreenHoverFrameTop ):
|
|||
|
||||
def _PopulateCenterButtons( self ):
|
||||
|
||||
# make a merge_options serialisable object to track all this that I can plug into the dialog and obey at the db level
|
||||
# store two in the options, for better and same, and then the custom just displays/produces one depending on chosen status
|
||||
|
||||
# cog icon launches a dialog
|
||||
# file delete on better
|
||||
# extend these to be per-service!
|
||||
# rating merging on better (d NO) (these are mutually exclusive, so add a radio menu type or whatever)
|
||||
# rating moving on better (d YES)
|
||||
# rating merging on same (d YES)
|
||||
# tag merging on better (d NO) (these are mutually exclusive, so add a radio menu type or whatever)
|
||||
# tag moving on better (d YES)
|
||||
# tag merging on same (d YES)
|
||||
|
||||
menu_items = []
|
||||
|
||||
menu_items.append( ( 'normal', 'edit tag/ratings merge options and whether to delete bad files', 'edit what happens when you filter files', self._EditMergeOptions ) )
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'this is better\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_BETTER ) ) )
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'exact duplicates\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_SAME_FILE ) ) )
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'alternates\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_ALTERNATE ) ) )
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'not duplicates\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_NOT_DUPLICATE ) ) )
|
||||
|
||||
cog_button = ClientGUICommon.MenuBitmapButton( self, CC.GlobalBMPs.cog, menu_items )
|
||||
|
||||
|
@ -523,9 +514,25 @@ class FullscreenHoverFrameTopDuplicatesFilter( FullscreenHoverFrameTop ):
|
|||
FullscreenHoverFrameTop._PopulateCenterButtons( self )
|
||||
|
||||
|
||||
def _EditMergeOptions( self ):
|
||||
def _EditMergeOptions( self, duplicate_status ):
|
||||
|
||||
wx.MessageBox( 'This doesn\'t do anything yet!' )
|
||||
new_options = HydrusGlobals.client_controller.GetNewOptions()
|
||||
|
||||
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_status )
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit duplicate merge options' ) as dlg:
|
||||
|
||||
panel = ClientGUIScrolledPanelsEdit.EditDuplicateActionOptionsPanel( dlg, duplicate_status, duplicate_action_options )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
duplicate_action_options = panel.GetValue()
|
||||
|
||||
new_options.SetDuplicateActionOptions( duplicate_status, duplicate_action_options )
|
||||
|
||||
|
||||
|
||||
|
||||
def _PopulateLeftButtons( self ):
|
||||
|
@ -601,6 +608,11 @@ class FullscreenHoverFrameRatings( FullscreenHoverFrame ):
|
|||
|
||||
self._file_repos = wx.StaticText( self, label = '', style = wx.ALIGN_RIGHT )
|
||||
|
||||
# urls
|
||||
|
||||
self._last_seen_urls = []
|
||||
self._urls_vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
# likes
|
||||
|
||||
like_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
@ -622,6 +634,7 @@ class FullscreenHoverFrameRatings( FullscreenHoverFrame ):
|
|||
|
||||
vbox.AddF( self._icon_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
vbox.AddF( self._file_repos, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( self._urls_vbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
vbox.AddF( like_hbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
|
||||
numerical_services = HydrusGlobals.client_controller.GetServicesManager().GetServices( ( HC.LOCAL_RATING_NUMERICAL, ), randomised = False )
|
||||
|
@ -716,6 +729,32 @@ class FullscreenHoverFrameRatings( FullscreenHoverFrame ):
|
|||
self._file_repos.Show()
|
||||
|
||||
|
||||
# urls
|
||||
|
||||
urls = self._current_media.GetLocationsManager().GetURLs()
|
||||
|
||||
urls = list( urls )
|
||||
|
||||
urls.sort()
|
||||
|
||||
if urls != self._last_seen_urls:
|
||||
|
||||
self._last_seen_urls = list( urls )
|
||||
|
||||
self._urls_vbox.Clear( deleteWindows = True )
|
||||
|
||||
for url in urls:
|
||||
|
||||
parse = urlparse.urlparse( url )
|
||||
|
||||
url_string = parse.scheme + '://' + parse.hostname
|
||||
|
||||
link = wx.HyperlinkCtrl( self, id = -1, label = url_string, url = url )
|
||||
|
||||
self._urls_vbox.AddF( link, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
||||
|
||||
self.Fit()
|
||||
|
||||
|
||||
|
|
|
@ -1150,9 +1150,9 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
num_unknown = duplicate_types_to_count[ HC.DUPLICATE_UNKNOWN ]
|
||||
|
||||
self._num_unknown_duplicates.SetLabelText( HydrusData.ConvertIntToPrettyString( num_unknown ) + ' potential matches found.' )
|
||||
self._num_better_duplicates.SetLabelText( HydrusData.ConvertIntToPrettyString( duplicate_types_to_count[ HC.DUPLICATE_BETTER ] ) + ' better/worse pairs filtered.' )
|
||||
self._num_same_file_duplicates.SetLabelText( HydrusData.ConvertIntToPrettyString( duplicate_types_to_count[ HC.DUPLICATE_SAME_FILE ] ) + ' exactly similar pairs filtered.' )
|
||||
self._num_alternate_duplicates.SetLabelText( HydrusData.ConvertIntToPrettyString( duplicate_types_to_count[ HC.DUPLICATE_ALTERNATE ] ) + ' alternate pairs filtered.' )
|
||||
self._num_better_duplicates.SetLabelText( HydrusData.ConvertIntToPrettyString( duplicate_types_to_count[ HC.DUPLICATE_BETTER ] ) + ' better/worse pairs found.' )
|
||||
self._num_same_file_duplicates.SetLabelText( HydrusData.ConvertIntToPrettyString( duplicate_types_to_count[ HC.DUPLICATE_SAME_FILE ] ) + ' exactly similar pairs found.' )
|
||||
self._num_alternate_duplicates.SetLabelText( HydrusData.ConvertIntToPrettyString( duplicate_types_to_count[ HC.DUPLICATE_ALTERNATE ] ) + ' alternate pairs found.' )
|
||||
|
||||
if num_unknown > 0:
|
||||
|
||||
|
@ -2238,7 +2238,7 @@ class ManagementPanelPetitions( ManagementPanel ):
|
|||
check_all = ClientGUICommon.BetterButton( self._petition_panel, 'check all', self._CheckAll )
|
||||
check_none = ClientGUICommon.BetterButton( self._petition_panel, 'check none', self._CheckNone )
|
||||
|
||||
self._contents = wx.CheckListBox( self._petition_panel, size = ( -1, 300 ) )
|
||||
self._contents = wx.CheckListBox( self._petition_panel )
|
||||
self._contents.Bind( wx.EVT_LISTBOX_DCLICK, self.EventContentDoubleClick )
|
||||
|
||||
self._process = wx.Button( self._petition_panel, label = 'process' )
|
||||
|
@ -2279,7 +2279,7 @@ class ManagementPanelPetitions( ManagementPanel ):
|
|||
self._MakeCollect( vbox )
|
||||
|
||||
vbox.AddF( self._petitions_info_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._petition_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._petition_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self._MakeCurrentSelectionTagsBox( vbox )
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import wx
|
|||
import yaml
|
||||
import HydrusData
|
||||
import HydrusGlobals
|
||||
import webbrowser
|
||||
|
||||
# Option Enums
|
||||
|
||||
|
@ -224,24 +225,6 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', hex_hashes )
|
||||
|
||||
|
||||
def _CopyKnownURLsToClipboard( self ):
|
||||
|
||||
hash = self._focussed_media.GetDisplayMedia().GetHash()
|
||||
|
||||
known_urls = HydrusGlobals.client_controller.Read( 'known_urls', hash )
|
||||
|
||||
if len( known_urls ) == 0:
|
||||
|
||||
HydrusData.ShowText( 'No known urls!' )
|
||||
|
||||
else:
|
||||
|
||||
text = os.linesep.join( known_urls )
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
|
||||
|
||||
|
||||
|
||||
def _CopyPathToClipboard( self ):
|
||||
|
||||
display_media = self._focussed_media.GetDisplayMedia()
|
||||
|
@ -2167,7 +2150,6 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
elif command == 'copy_files': self._CopyFilesToClipboard()
|
||||
elif command == 'copy_hash': self._CopyHashToClipboard( data )
|
||||
elif command == 'copy_hashes': self._CopyHashesToClipboard( data )
|
||||
elif command == 'copy_known_urls': self._CopyKnownURLsToClipboard()
|
||||
elif command == 'copy_hashes': self._CopyHashesToClipboard( data )
|
||||
elif command == 'copy_service_filename': self._CopyServiceFilenameToClipboard( data )
|
||||
elif command == 'copy_service_filenames': self._CopyServiceFilenamesToClipboard( data )
|
||||
|
@ -2860,7 +2842,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
ClientGUIMenus.AppendMenuItem( self, menu, undelete_phrase, 'Restore the selected files back to \'my files\'.', self._Undelete )
|
||||
|
||||
|
||||
# share
|
||||
#
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
|
@ -2869,6 +2851,35 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
ClientGUIMenus.AppendMenuItem( self, menu, 'open externally', 'Launch this file with your OS\'s default program for it.', self._OpenExternally )
|
||||
|
||||
|
||||
#
|
||||
|
||||
urls = self._focussed_media.GetLocationsManager().GetURLs()
|
||||
|
||||
if len( urls ) > 0:
|
||||
|
||||
urls = list( urls )
|
||||
|
||||
urls.sort()
|
||||
|
||||
urls_menu = wx.Menu()
|
||||
|
||||
urls_visit_menu = wx.Menu()
|
||||
urls_copy_menu = wx.Menu()
|
||||
|
||||
for url in urls:
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, urls_visit_menu, url, 'Open this url in your web browser.', webbrowser.open, url )
|
||||
ClientGUIMenus.AppendMenuItem( self, urls_copy_menu, url, 'Copy this url to your clipboard.', HydrusGlobals.client_controller.pub, 'clipboard', 'text', url )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenu( urls_menu, urls_visit_menu, 'open' )
|
||||
ClientGUIMenus.AppendMenu( urls_menu, urls_copy_menu, 'copy' )
|
||||
|
||||
ClientGUIMenus.AppendMenu( menu, urls_menu, 'known urls' )
|
||||
|
||||
|
||||
# share
|
||||
|
||||
share_menu = wx.Menu()
|
||||
|
||||
#
|
||||
|
@ -2942,8 +2953,6 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
ClientGUIMenus.AppendMenuItem( self, copy_menu, 'paths', 'Copy the selected files\' paths to the clipboard.', self._CopyPathsToClipboard )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, copy_menu, 'known urls (prototype)', 'Copy the selected file\'s known urls to the clipboard.', self._CopyKnownURLsToClipboard )
|
||||
|
||||
ClientGUIMenus.AppendMenu( share_menu, copy_menu, 'copy' )
|
||||
|
||||
#
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import ClientConstants as CC
|
||||
import ClientData
|
||||
import ClientDefaults
|
||||
import ClientDownloading
|
||||
import ClientImporting
|
||||
|
@ -132,6 +133,221 @@ class EditAccountTypePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return HydrusNetwork.AccountType.GenerateAccountTypeFromParameters( self._account_type_key, title, permissions, bandwidth_rules )
|
||||
|
||||
|
||||
class EditDuplicateActionOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, duplicate_action, duplicate_action_options ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
self._duplicate_action = duplicate_action
|
||||
|
||||
self._service_actions = ClientGUICommon.SaneListCtrl( self, 120, [ ( 'service name', -1 ), ( 'action', 240 ) ], delete_key_callback = self._Delete, activation_callback = self._Edit )
|
||||
|
||||
self._service_actions.SetMinSize( ( 320, 120 ) )
|
||||
|
||||
add_button = ClientGUICommon.BetterButton( self, 'add', self._Add )
|
||||
edit_button = ClientGUICommon.BetterButton( self, 'edit', self._Edit )
|
||||
delete_button = ClientGUICommon.BetterButton( self, 'delete', self._Delete )
|
||||
|
||||
self._delete_second_file = wx.CheckBox( self, label = 'delete worse file' )
|
||||
|
||||
#
|
||||
|
||||
( service_actions, delete_second_file ) = duplicate_action_options.ToTuple()
|
||||
|
||||
services_manager = HydrusGlobals.client_controller.GetServicesManager()
|
||||
|
||||
for ( service_key, action ) in service_actions:
|
||||
|
||||
if services_manager.ServiceExists( service_key ):
|
||||
|
||||
sort_tuple = ( service_key, action )
|
||||
|
||||
display_tuple = self._GetDisplayTuple( sort_tuple )
|
||||
|
||||
self._service_actions.Append( display_tuple, sort_tuple )
|
||||
|
||||
|
||||
|
||||
self._delete_second_file.SetValue( delete_second_file )
|
||||
|
||||
#
|
||||
|
||||
if self._duplicate_action != HC.DUPLICATE_BETTER:
|
||||
|
||||
self._delete_second_file.Hide()
|
||||
edit_button.Hide()
|
||||
|
||||
|
||||
button_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
button_hbox.AddF( add_button, CC.FLAGS_VCENTER )
|
||||
button_hbox.AddF( edit_button, CC.FLAGS_VCENTER )
|
||||
button_hbox.AddF( delete_button, CC.FLAGS_VCENTER )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._service_actions, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( button_hbox, CC.FLAGS_BUTTON_SIZER )
|
||||
vbox.AddF( self._delete_second_file, CC.FLAGS_LONE_BUTTON )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def _Add( self ):
|
||||
|
||||
existing_service_keys = set()
|
||||
|
||||
for ( service_key, action ) in self._service_actions.GetClientData():
|
||||
|
||||
existing_service_keys.add( service_key )
|
||||
|
||||
|
||||
services_manager = HydrusGlobals.client_controller.GetServicesManager()
|
||||
|
||||
choice_tuples = []
|
||||
|
||||
for service in services_manager.GetServices( [ HC.LOCAL_TAG, HC.TAG_REPOSITORY, HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ] ):
|
||||
|
||||
service_key = service.GetServiceKey()
|
||||
|
||||
if service_key not in existing_service_keys:
|
||||
|
||||
name = service.GetName()
|
||||
|
||||
choice_tuples.append( ( name, service_key ) )
|
||||
|
||||
|
||||
|
||||
if len( choice_tuples ) == 0:
|
||||
|
||||
wx.MessageBox( 'You have no more tag or rating services to add! Try editing the existing ones instead!' )
|
||||
|
||||
else:
|
||||
|
||||
with ClientGUIDialogs.DialogSelectFromList( self, 'select service', choice_tuples ) as dlg_1:
|
||||
|
||||
if dlg_1.ShowModal() == wx.ID_OK:
|
||||
|
||||
service_key = dlg_1.GetChoice()
|
||||
|
||||
if self._duplicate_action == HC.DUPLICATE_BETTER:
|
||||
|
||||
service = services_manager.GetService( service_key )
|
||||
|
||||
if service.GetServiceType() == HC.TAG_REPOSITORY:
|
||||
|
||||
possible_actions = [ HC.CONTENT_MERGE_ACTION_COPY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ]
|
||||
|
||||
else:
|
||||
|
||||
possible_actions = [ HC.CONTENT_MERGE_ACTION_COPY, HC.CONTENT_MERGE_ACTION_MOVE, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ]
|
||||
|
||||
|
||||
choice_tuples = [ ( HC.content_merge_string_lookup[ action ], action ) for action in possible_actions ]
|
||||
|
||||
with ClientGUIDialogs.DialogSelectFromList( self, 'select action', choice_tuples ) as dlg_2:
|
||||
|
||||
if dlg_2.ShowModal() == wx.ID_OK:
|
||||
|
||||
action = dlg_2.GetChoice()
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
action = HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE
|
||||
|
||||
|
||||
sort_tuple = ( service_key, action )
|
||||
|
||||
display_tuple = self._GetDisplayTuple( sort_tuple )
|
||||
|
||||
self._service_actions.Append( display_tuple, sort_tuple )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _Delete( self ):
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'Remove all selected?' ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
self._service_actions.RemoveAllSelected()
|
||||
|
||||
|
||||
|
||||
|
||||
def _Edit( self ):
|
||||
|
||||
all_selected = self._service_actions.GetAllSelected()
|
||||
|
||||
for index in all_selected:
|
||||
|
||||
( service_key, action ) = self._service_actions.GetClientData( index )
|
||||
|
||||
if self._duplicate_action == HC.DUPLICATE_BETTER:
|
||||
|
||||
possible_actions = [ HC.CONTENT_MERGE_ACTION_COPY, HC.CONTENT_MERGE_ACTION_MOVE, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ]
|
||||
|
||||
choice_tuples = [ ( HC.content_merge_string_lookup[ action ], action ) for action in possible_actions ]
|
||||
|
||||
with ClientGUIDialogs.DialogSelectFromList( self, 'select action', choice_tuples ) as dlg_2:
|
||||
|
||||
if dlg_2.ShowModal() == wx.ID_OK:
|
||||
|
||||
action = dlg_2.GetChoice()
|
||||
|
||||
else:
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
else: # This shouldn't get fired because the edit button is hidden, but w/e
|
||||
|
||||
action = HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE
|
||||
|
||||
|
||||
sort_tuple = ( service_key, action )
|
||||
|
||||
display_tuple = self._GetDisplayTuple( sort_tuple )
|
||||
|
||||
self._service_actions.UpdateRow( index, display_tuple, sort_tuple )
|
||||
|
||||
|
||||
|
||||
def _GetDisplayTuple( self, sort_tuple ):
|
||||
|
||||
( service_key, action ) = sort_tuple
|
||||
|
||||
services_manager = HydrusGlobals.client_controller.GetServicesManager()
|
||||
|
||||
service = services_manager.GetService( service_key )
|
||||
|
||||
name = service.GetName()
|
||||
|
||||
pretty_action = HC.content_merge_string_lookup[ action ]
|
||||
|
||||
return ( name, pretty_action )
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
service_actions = self._service_actions.GetClientData()
|
||||
delete_second_file = self._delete_second_file.GetValue()
|
||||
|
||||
duplicate_action_options = ClientData.DuplicateActionOptions( service_actions, delete_second_file )
|
||||
|
||||
return duplicate_action_options
|
||||
|
||||
|
||||
class EditFrameLocationPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, info ):
|
||||
|
|
|
@ -3836,7 +3836,7 @@ class ManageShortcutsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
|
||||
|
||||
def _Delete( self, event ):
|
||||
def _Delete( self ):
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'Remove all selected?' ) as dlg:
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ class AdvancedContentUpdatePanel( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
COPY = 0
|
||||
DELETE = 1
|
||||
DELETE_DELETED = 2
|
||||
DELETE_FOR_DELETED_FILES = 3
|
||||
|
||||
ALL_MAPPINGS = 0
|
||||
SPECIFIC_MAPPINGS = 1
|
||||
|
@ -67,6 +68,7 @@ class AdvancedContentUpdatePanel( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
|
||||
self._action_dropdown.Append( 'delete', self.DELETE )
|
||||
self._action_dropdown.Append( 'clear deleted record', self.DELETE_DELETED )
|
||||
self._action_dropdown.Append( 'delete from deleted files', self.DELETE_FOR_DELETED_FILES )
|
||||
|
||||
|
||||
self._action_dropdown.Select( 0 )
|
||||
|
@ -150,7 +152,7 @@ class AdvancedContentUpdatePanel( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
|
||||
data = self._action_dropdown.GetChoice()
|
||||
|
||||
if data in ( self.DELETE, self.DELETE_DELETED ):
|
||||
if data in ( self.DELETE, self.DELETE_DELETED, self.DELETE_FOR_DELETED_FILES ):
|
||||
|
||||
self._action_text.SetLabelText( 'from ' + self._service_name )
|
||||
|
||||
|
@ -246,6 +248,10 @@ class AdvancedContentUpdatePanel( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
|
||||
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADVANCED, ( 'delete_deleted', ( tag, self._hashes ) ) )
|
||||
|
||||
elif action == self.DELETE_FOR_DELETED_FILES:
|
||||
|
||||
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADVANCED, ( 'delete_for_deleted_files', ( tag, self._hashes ) ) )
|
||||
|
||||
|
||||
service_keys_to_content_updates = { self._service_key : [ content_update ] }
|
||||
|
||||
|
|
|
@ -1174,6 +1174,13 @@ class ImportFolder( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
txt_tags = [ HydrusData.ToUnicode( tag ) for tag in HydrusData.SplitByLinesep( txt_tags_string ) ]
|
||||
|
||||
if True in ( len( txt_tag ) > 1024 for txt_tag in txt_tags ):
|
||||
|
||||
HydrusData.ShowText( 'Tags were too long--I think this was not a regular text file!' )
|
||||
|
||||
raise Exception()
|
||||
|
||||
|
||||
txt_tags = HydrusTags.CleanTags( txt_tags )
|
||||
|
||||
service_keys_to_tags = { service_key : txt_tags for service_key in self._txt_parse_tag_service_keys }
|
||||
|
@ -1498,12 +1505,12 @@ class PageOfImagesImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
if self._download_image_links:
|
||||
|
||||
file_urls.extend( [ urlparse.urljoin( page_url, link[ 'href' ] ) for link in links_with_images ] )
|
||||
file_urls.extend( [ urlparse.urljoin( page_url, link[ 'href' ] ) for link in links_with_images if link.has_attr( 'href' ) ] )
|
||||
|
||||
|
||||
if self._download_unlinked_images:
|
||||
|
||||
file_urls.extend( [ urlparse.urljoin( page_url, image[ 'src' ] ) for image in unlinked_images ] )
|
||||
file_urls.extend( [ urlparse.urljoin( page_url, image[ 'src' ] ) for image in unlinked_images if image.has_attr( 'src' ) ] )
|
||||
|
||||
|
||||
num_new = 0
|
||||
|
|
|
@ -49,7 +49,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 18
|
||||
SOFTWARE_VERSION = 252
|
||||
SOFTWARE_VERSION = 253
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
@ -70,10 +70,15 @@ bandwidth_type_string_lookup = {}
|
|||
bandwidth_type_string_lookup[ BANDWIDTH_TYPE_DATA ] = 'data'
|
||||
bandwidth_type_string_lookup[ BANDWIDTH_TYPE_REQUESTS ] = 'requests'
|
||||
|
||||
CONTENT_MERGE_ACTION_DO_NOTHING = 0
|
||||
CONTENT_MERGE_ACTION_COPY = 1
|
||||
CONTENT_MERGE_ACTION_MOVE = 2
|
||||
CONTENT_MERGE_ACTION_UNION = 3
|
||||
CONTENT_MERGE_ACTION_COPY = 0
|
||||
CONTENT_MERGE_ACTION_MOVE = 1
|
||||
CONTENT_MERGE_ACTION_TWO_WAY_MERGE = 2
|
||||
|
||||
content_merge_string_lookup = {}
|
||||
|
||||
content_merge_string_lookup[ CONTENT_MERGE_ACTION_COPY ] = 'copy from worse to better'
|
||||
content_merge_string_lookup[ CONTENT_MERGE_ACTION_MOVE ] = 'move from worse to better'
|
||||
content_merge_string_lookup[ CONTENT_MERGE_ACTION_TWO_WAY_MERGE ] = 'copy in both directions'
|
||||
|
||||
CONTENT_STATUS_CURRENT = 0
|
||||
CONTENT_STATUS_PENDING = 1
|
||||
|
@ -168,6 +173,17 @@ DUPLICATE_SMALLER_BETTER = 5
|
|||
DUPLICATE_LARGER_BETTER = 6
|
||||
DUPLICATE_WORSE = 7
|
||||
|
||||
duplicate_status_string_lookup = {}
|
||||
|
||||
duplicate_status_string_lookup[ DUPLICATE_UNKNOWN ] = 'unknown relationship'
|
||||
duplicate_status_string_lookup[ DUPLICATE_NOT_DUPLICATE ] = 'not duplicates'
|
||||
duplicate_status_string_lookup[ DUPLICATE_SAME_FILE ] = 'exact same files'
|
||||
duplicate_status_string_lookup[ DUPLICATE_ALTERNATE ] = 'alternates'
|
||||
duplicate_status_string_lookup[ DUPLICATE_BETTER ] = 'this is better'
|
||||
duplicate_status_string_lookup[ DUPLICATE_SMALLER_BETTER ] = 'smaller hash_id is better'
|
||||
duplicate_status_string_lookup[ DUPLICATE_LARGER_BETTER ] = 'larger hash_id is better'
|
||||
duplicate_status_string_lookup[ DUPLICATE_WORSE ] = 'this is worse'
|
||||
|
||||
ENCODING_RAW = 0
|
||||
ENCODING_HEX = 1
|
||||
ENCODING_BASE64 = 2
|
||||
|
|
|
@ -1479,11 +1479,22 @@ class ContentUpdate( object ):
|
|||
self._row = row
|
||||
|
||||
|
||||
def __eq__( self, other ): return self._data_type == other._data_type and self._action == other._action and self._row == other._row
|
||||
def __eq__( self, other ):
|
||||
|
||||
return hash( self ) == hash( other )
|
||||
|
||||
|
||||
def __ne__( self, other ): return not self.__eq__( other )
|
||||
|
||||
def __repr__( self ): return 'Content Update: ' + ToUnicode( ( self._data_type, self._action, self._row ) )
|
||||
def __hash__( self ):
|
||||
|
||||
return hash( ( self._data_type, self._action, repr( self._row ) ) )
|
||||
|
||||
|
||||
def __repr__( self ):
|
||||
|
||||
return 'Content Update: ' + ToUnicode( ( self._data_type, self._action, self._row ) )
|
||||
|
||||
|
||||
def GetHashes( self ):
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ SERIALISABLE_TYPE_BANDWIDTH_TRACKER = 39
|
|||
SERIALISABLE_TYPE_CLIENT_TO_SERVER_UPDATE = 40
|
||||
SERIALISABLE_TYPE_SHORTCUT = 41
|
||||
SERIALISABLE_TYPE_APPLICATION_COMMAND = 42
|
||||
SERIALISABLE_TYPE_DUPLICATE_ACTION_OPTIONS = 43
|
||||
|
||||
SERIALISABLE_TYPES_TO_OBJECT_TYPES = {}
|
||||
|
||||
|
|
|
@ -386,10 +386,10 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
interrupt_received = True
|
||||
|
||||
HydrusData.Print( u'Received a keyboard interrupt\u2026' )
|
||||
|
||||
def do_it():
|
||||
|
||||
HydrusData.Print( u'Received a keyboard interrupt\u2026' )
|
||||
|
||||
self.Exit()
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,20 @@ DB_DIR = None
|
|||
|
||||
tinest_gif = '\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x00\xFF\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x00\x3B'
|
||||
|
||||
LOCAL_RATING_LIKE_SERVICE_KEY = HydrusData.GenerateKey()
|
||||
LOCAL_RATING_NUMERICAL_SERVICE_KEY = HydrusData.GenerateKey()
|
||||
|
||||
def ConvertServiceKeysToContentUpdatesToComparable( service_keys_to_content_updates ):
|
||||
|
||||
comparable_dict = {}
|
||||
|
||||
for ( service_key, content_updates ) in service_keys_to_content_updates.items():
|
||||
|
||||
comparable_dict[ service_key ] = set( content_updates )
|
||||
|
||||
|
||||
return comparable_dict
|
||||
|
||||
class FakeHTTPConnectionManager():
|
||||
|
||||
def __init__( self ):
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import ClientCaches
|
||||
import ClientConstants as CC
|
||||
import ClientData
|
||||
import ClientDefaults
|
||||
import ClientDownloading
|
||||
import ClientImporting
|
||||
import ClientMedia
|
||||
import ClientRatings
|
||||
import ClientSearch
|
||||
import HydrusConstants as HC
|
||||
import HydrusData
|
||||
import HydrusNetwork
|
||||
import HydrusSerialisable
|
||||
import TestConstants as TC
|
||||
import os
|
||||
import unittest
|
||||
import wx
|
||||
|
@ -142,6 +146,214 @@ class TestSerialisables( unittest.TestCase ):
|
|||
|
||||
|
||||
|
||||
def test_SERIALISABLE_TYPE_DUPLICATE_ACTION_OPTIONS( self ):
|
||||
|
||||
def test( obj, dupe_obj ):
|
||||
|
||||
self.assertEqual( obj.ToTuple(), dupe_obj.ToTuple() )
|
||||
|
||||
|
||||
duplicate_action_options_delete_and_move = ClientData.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ), ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ) ], True )
|
||||
duplicate_action_options_copy = ClientData.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ), ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ) ], False )
|
||||
duplicate_action_options_merge = ClientData.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ), ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ) ], False )
|
||||
|
||||
inbox = True
|
||||
size = 40960
|
||||
mime = HC.IMAGE_JPEG
|
||||
width = 640
|
||||
height = 480
|
||||
duration = None
|
||||
num_frames = None
|
||||
num_words = None
|
||||
|
||||
local_locations_manager = ClientMedia.LocationsManager( { CC.LOCAL_FILE_SERVICE_KEY, CC.COMBINED_LOCAL_FILE_SERVICE_KEY }, set(), set(), set() )
|
||||
trash_locations_manager = ClientMedia.LocationsManager( { CC.TRASH_SERVICE_KEY, CC.COMBINED_LOCAL_FILE_SERVICE_KEY }, set(), set(), set() )
|
||||
deleted_locations_manager = ClientMedia.LocationsManager( set(), { CC.COMBINED_LOCAL_FILE_SERVICE_KEY }, set(), set() )
|
||||
|
||||
# duplicate to generate proper dicts
|
||||
|
||||
one_tags_manager = ClientMedia.TagsManager( { CC.LOCAL_TAG_SERVICE_KEY : { HC.CONTENT_STATUS_CURRENT : { 'one' } } } ).Duplicate()
|
||||
two_tags_manager = ClientMedia.TagsManager( { CC.LOCAL_TAG_SERVICE_KEY : { HC.CONTENT_STATUS_CURRENT : { 'two' } } } ).Duplicate()
|
||||
substantial_tags_manager = ClientMedia.TagsManager( { CC.LOCAL_TAG_SERVICE_KEY : { HC.CONTENT_STATUS_CURRENT : { 'test tag', 'series:namespaced test tag' } } } ).Duplicate()
|
||||
empty_tags_manager = ClientMedia.TagsManager( {} ).Duplicate()
|
||||
|
||||
one_ratings_manager = ClientRatings.RatingsManager( { TC.LOCAL_RATING_LIKE_SERVICE_KEY : 1.0, TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY : 0.8 } )
|
||||
two_ratings_manager = ClientRatings.RatingsManager( { TC.LOCAL_RATING_LIKE_SERVICE_KEY : 0.0, TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY : 0.6 } )
|
||||
substantial_ratings_manager = ClientRatings.RatingsManager( { TC.LOCAL_RATING_LIKE_SERVICE_KEY : 1.0, TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY : 0.8 } )
|
||||
empty_ratings_manager = ClientRatings.RatingsManager( {} )
|
||||
|
||||
local_hash_has_values = HydrusData.GenerateKey()
|
||||
|
||||
media_result = ClientMedia.MediaResult( ( local_hash_has_values, inbox, size, mime, width, height, duration, num_frames, num_words, substantial_tags_manager, local_locations_manager, substantial_ratings_manager ) )
|
||||
|
||||
local_media_has_values = ClientMedia.MediaSingleton( media_result )
|
||||
|
||||
other_local_hash_has_values = HydrusData.GenerateKey()
|
||||
|
||||
media_result = ClientMedia.MediaResult( ( other_local_hash_has_values, inbox, size, mime, width, height, duration, num_frames, num_words, substantial_tags_manager, local_locations_manager, substantial_ratings_manager ) )
|
||||
|
||||
other_local_media_has_values = ClientMedia.MediaSingleton( media_result )
|
||||
|
||||
local_hash_empty = HydrusData.GenerateKey()
|
||||
|
||||
media_result = ClientMedia.MediaResult( ( local_hash_empty, inbox, size, mime, width, height, duration, num_frames, num_words, empty_tags_manager, local_locations_manager, empty_ratings_manager ) )
|
||||
|
||||
local_media_empty = ClientMedia.MediaSingleton( media_result )
|
||||
|
||||
trashed_hash_empty = HydrusData.GenerateKey()
|
||||
|
||||
media_result = ClientMedia.MediaResult( ( trashed_hash_empty, inbox, size, mime, width, height, duration, num_frames, num_words, empty_tags_manager, trash_locations_manager, empty_ratings_manager ) )
|
||||
|
||||
trashed_media_empty = ClientMedia.MediaSingleton( media_result )
|
||||
|
||||
deleted_hash_empty = HydrusData.GenerateKey()
|
||||
|
||||
media_result = ClientMedia.MediaResult( ( deleted_hash_empty, inbox, size, mime, width, height, duration, num_frames, num_words, empty_tags_manager, deleted_locations_manager, empty_ratings_manager ) )
|
||||
|
||||
deleted_media_empty = ClientMedia.MediaSingleton( media_result )
|
||||
|
||||
one_hash = HydrusData.GenerateKey()
|
||||
|
||||
media_result = ClientMedia.MediaResult( ( one_hash, inbox, size, mime, width, height, duration, num_frames, num_words, one_tags_manager, local_locations_manager, one_ratings_manager ) )
|
||||
|
||||
one_media = ClientMedia.MediaSingleton( media_result )
|
||||
|
||||
two_hash = HydrusData.GenerateKey()
|
||||
|
||||
media_result = ClientMedia.MediaResult( ( two_hash, inbox, size, mime, width, height, duration, num_frames, num_words, two_tags_manager, local_locations_manager, two_ratings_manager ) )
|
||||
|
||||
two_media = ClientMedia.MediaSingleton( media_result )
|
||||
|
||||
#
|
||||
|
||||
self._dump_and_load_and_test( duplicate_action_options_delete_and_move, test )
|
||||
self._dump_and_load_and_test( duplicate_action_options_copy, test )
|
||||
self._dump_and_load_and_test( duplicate_action_options_merge, test )
|
||||
|
||||
#
|
||||
|
||||
def assertSCUEqual( one, two ):
|
||||
|
||||
self.assertEqual( TC.ConvertServiceKeysToContentUpdatesToComparable( one ), TC.ConvertServiceKeysToContentUpdatesToComparable( two ) )
|
||||
|
||||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, local_media_empty )
|
||||
|
||||
scu = {}
|
||||
|
||||
scu[ CC.LOCAL_FILE_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, { local_hash_empty } ) ]
|
||||
|
||||
assertSCUEqual( result[0], scu )
|
||||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, trashed_media_empty )
|
||||
|
||||
scu = {}
|
||||
|
||||
scu[ CC.TRASH_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, { trashed_hash_empty } ) ]
|
||||
|
||||
assertSCUEqual( result[0], scu )
|
||||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, deleted_media_empty )
|
||||
|
||||
self.assertEqual( result, [] )
|
||||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, other_local_media_has_values )
|
||||
|
||||
scu = {}
|
||||
|
||||
scu[ CC.LOCAL_TAG_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE, ( 'test tag', { other_local_hash_has_values } ) ), HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE, ( 'series:namespaced test tag', { other_local_hash_has_values } ) ) ]
|
||||
scu[ TC.LOCAL_RATING_LIKE_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( None, { other_local_hash_has_values } ) ) ]
|
||||
scu[ TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( None, { other_local_hash_has_values } ) ) ]
|
||||
|
||||
assertSCUEqual( result[0], scu )
|
||||
|
||||
scu = {}
|
||||
|
||||
scu[ CC.LOCAL_FILE_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, { other_local_hash_has_values } ) ]
|
||||
|
||||
assertSCUEqual( result[1], scu )
|
||||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_empty, other_local_media_has_values )
|
||||
|
||||
scu = {}
|
||||
|
||||
scu[ CC.LOCAL_TAG_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'test tag', { local_hash_empty } ) ), HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'series:namespaced test tag', { local_hash_empty } ) ), HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE, ( 'test tag', { other_local_hash_has_values } ) ), HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE, ( 'series:namespaced test tag', { other_local_hash_has_values } ) ) ]
|
||||
scu[ TC.LOCAL_RATING_LIKE_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( 1.0, { local_hash_empty } ) ), HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( None, { other_local_hash_has_values } ) ) ]
|
||||
scu[ TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( 0.8, { local_hash_empty } ) ), HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( None, { other_local_hash_has_values } ) ) ]
|
||||
|
||||
assertSCUEqual( result[0], scu )
|
||||
|
||||
scu = {}
|
||||
|
||||
scu[ CC.LOCAL_FILE_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, { other_local_hash_has_values } ) ]
|
||||
|
||||
assertSCUEqual( result[1], scu )
|
||||
|
||||
#
|
||||
#
|
||||
|
||||
result = duplicate_action_options_copy.ProcessPairIntoContentUpdates( local_media_has_values, local_media_empty )
|
||||
|
||||
self.assertEqual( result, [] )
|
||||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_copy.ProcessPairIntoContentUpdates( local_media_empty, other_local_media_has_values )
|
||||
|
||||
scu = {}
|
||||
|
||||
scu[ CC.LOCAL_TAG_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'test tag', { local_hash_empty } ) ), HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'series:namespaced test tag', { local_hash_empty } ) ) ]
|
||||
scu[ TC.LOCAL_RATING_LIKE_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( 1.0, { local_hash_empty } ) ) ]
|
||||
scu[ TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( 0.8, { local_hash_empty } ) ) ]
|
||||
|
||||
assertSCUEqual( result[0], scu )
|
||||
|
||||
#
|
||||
#
|
||||
|
||||
result = duplicate_action_options_merge.ProcessPairIntoContentUpdates( local_media_has_values, local_media_empty )
|
||||
|
||||
scu = {}
|
||||
|
||||
scu[ CC.LOCAL_TAG_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'test tag', { local_hash_empty } ) ), HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'series:namespaced test tag', { local_hash_empty } ) ) ]
|
||||
scu[ TC.LOCAL_RATING_LIKE_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( 1.0, { local_hash_empty } ) ) ]
|
||||
scu[ TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( 0.8, { local_hash_empty } ) ) ]
|
||||
|
||||
assertSCUEqual( result[0], scu )
|
||||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_merge.ProcessPairIntoContentUpdates( local_media_empty, other_local_media_has_values )
|
||||
|
||||
scu = {}
|
||||
|
||||
scu[ CC.LOCAL_TAG_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'test tag', { local_hash_empty } ) ), HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'series:namespaced test tag', { local_hash_empty } ) ) ]
|
||||
scu[ TC.LOCAL_RATING_LIKE_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( 1.0, { local_hash_empty } ) ) ]
|
||||
scu[ TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( 0.8, { local_hash_empty } ) ) ]
|
||||
|
||||
assertSCUEqual( result[0], scu )
|
||||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_merge.ProcessPairIntoContentUpdates( one_media, two_media )
|
||||
|
||||
scu = {}
|
||||
|
||||
scu[ CC.LOCAL_TAG_SERVICE_KEY ] = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'one', { two_hash } ) ), HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, ( 'two', { one_hash } ) ) ]
|
||||
|
||||
assertSCUEqual( result[0], scu )
|
||||
|
||||
|
||||
def test_SERIALISABLE_TYPE_SHORTCUT( self ):
|
||||
|
||||
def test( obj, dupe_obj ):
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 786 B |
2
test.py
2
test.py
|
@ -96,6 +96,8 @@ class Controller( object ):
|
|||
services.append( ClientServices.GenerateService( CC.LOCAL_FILE_SERVICE_KEY, HC.LOCAL_FILE_DOMAIN, CC.LOCAL_FILE_SERVICE_KEY ) )
|
||||
services.append( ClientServices.GenerateService( CC.TRASH_SERVICE_KEY, HC.LOCAL_FILE_TRASH_DOMAIN, CC.LOCAL_FILE_SERVICE_KEY ) )
|
||||
services.append( ClientServices.GenerateService( CC.LOCAL_TAG_SERVICE_KEY, HC.LOCAL_TAG, CC.LOCAL_TAG_SERVICE_KEY ) )
|
||||
services.append( ClientServices.GenerateService( TestConstants.LOCAL_RATING_LIKE_SERVICE_KEY, HC.LOCAL_RATING_LIKE, 'example local rating like service' ) )
|
||||
services.append( ClientServices.GenerateService( TestConstants.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.LOCAL_RATING_NUMERICAL, 'example local rating numerical service' ) )
|
||||
|
||||
self._reads[ 'services' ] = services
|
||||
|
||||
|
|
Loading…
Reference in New Issue