Version 164
This commit is contained in:
parent
1e162828f4
commit
c985f70a4f
|
@ -8,6 +8,31 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 164</h3></li>
|
||||
<ul>
|
||||
<li>rewrote the drawing code for the listbox that displays tags in various ways to be a lot faster and more memory efficient</li>
|
||||
<li>updated one new client mapping index that wasn't working quite as I wanted it to something more clever</li>
|
||||
<li>db will be a little smaller and mappings stuff will be even faster</li>
|
||||
<li>merged the two ratings system predicate input panels, so you can now select like/dislike and numerical ratings system predicates at the same time</li>
|
||||
<li>fixed booru download page serialisation, which means they will save to sessions</li>
|
||||
<li>prototyped trash service</li>
|
||||
<li>locally deleted files will now be sent to trash</li>
|
||||
<li>locally deleted files will not be removed from the existing search</li>
|
||||
<li>files can be permanently deleted from trash, which will also immediately physically delete them from your hdd</li>
|
||||
<li>files can be restored from trash back to the local file service</li>
|
||||
<li>inbox state is now more separate from the local file service, so it will be remembered through a visit to the trash</li>
|
||||
<li>improved delete code all around</li>
|
||||
<li>general inbox/archive db code improvements</li>
|
||||
<li>misc content update pipeline improvements</li>
|
||||
<li>optimised mass-adding of files to a service (for instance, when (un)deleting a whole bunch of files!)</li>
|
||||
<li>delete orphans daemon is removed--it will be replaced by a more thorough single-shot hdd/db purge like 'check file integrity'</li>
|
||||
<li>files are not yet automatically removed from the trash--I will add that next week.</li>
|
||||
<li>updated db access info in db folder</li>
|
||||
<li>added sqlite command line executable to db folder for all platforms</li>
|
||||
<li>bit of code cleaning</li>
|
||||
<li>cleaned up some gui error reporting</li>
|
||||
<li>might have fixed a service cache bug in the db that was causing double bandwidth reports and possible looping sync behaviour</li>
|
||||
</ul>
|
||||
<li><h3>version 163</h3></li>
|
||||
<ul>
|
||||
<li>reconfigured some important mapping indices in the client db to reduce search time for many common tag operations</li>
|
||||
|
|
|
@ -232,6 +232,7 @@ class GlobalBMPs( object ):
|
|||
|
||||
GlobalBMPs.collection = wx.Bitmap( HC.STATIC_DIR + os.path.sep + 'collection.png' )
|
||||
GlobalBMPs.inbox = wx.Bitmap( HC.STATIC_DIR + os.path.sep + 'inbox.png' )
|
||||
GlobalBMPs.trash = wx.Bitmap( HC.STATIC_DIR + os.path.sep + 'trash.png' )
|
||||
|
||||
GlobalBMPs.archive = wx.Bitmap( HC.STATIC_DIR + os.path.sep + 'archive.png' )
|
||||
GlobalBMPs.to_inbox = wx.Bitmap( HC.STATIC_DIR + os.path.sep + 'to_inbox.png' )
|
||||
|
@ -265,6 +266,8 @@ LOCAL_FILE_SERVICE_KEY = 'local files'
|
|||
|
||||
LOCAL_BOORU_SERVICE_KEY = 'local booru'
|
||||
|
||||
TRASH_SERVICE_KEY = 'trash'
|
||||
|
||||
COMBINED_FILE_SERVICE_KEY = 'all known files'
|
||||
|
||||
COMBINED_TAG_SERVICE_KEY = 'all known tags'
|
||||
|
|
|
@ -286,11 +286,6 @@ class Controller( HydrusController.HydrusController ):
|
|||
if now - shutdown_timestamps[ CC.SHUTDOWN_TIMESTAMP_VACUUM ] > self._options[ 'maintenance_vacuum_period' ]: self.Write( 'vacuum' )
|
||||
|
||||
|
||||
if self._options[ 'maintenance_delete_orphans_period' ] != 0:
|
||||
|
||||
if now - shutdown_timestamps[ CC.SHUTDOWN_TIMESTAMP_DELETE_ORPHANS ] > self._options[ 'maintenance_delete_orphans_period' ]: self.Write( 'delete_orphans' )
|
||||
|
||||
|
||||
if self._timestamps[ 'last_service_info_cache_fatten' ] != 0 and now - self._timestamps[ 'last_service_info_cache_fatten' ] > 60 * 20:
|
||||
|
||||
HydrusGlobals.pubsub.pub( 'splash_set_text', 'fattening service info' )
|
||||
|
|
|
@ -992,41 +992,65 @@ class DB( HydrusDB.HydrusDB ):
|
|||
READ_WRITE_ACTIONS = [ 'service_info', 'system_predicates' ]
|
||||
WRITE_SPECIAL_ACTIONS = [ 'vacuum' ]
|
||||
|
||||
def _AddFile( self, service_id, hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words ):
|
||||
def _AddFiles( self, service_id, rows ):
|
||||
|
||||
result = self._c.execute( 'SELECT 1 FROM files_info WHERE service_id = ? AND hash_id = ?;', ( service_id, hash_id ) ).fetchone()
|
||||
successful_hash_ids = set()
|
||||
|
||||
if result is None:
|
||||
num_deleted = 0
|
||||
delta_size = 0
|
||||
num_thumbnails = 0
|
||||
num_inbox = 0
|
||||
|
||||
for ( hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words ) in rows:
|
||||
|
||||
self._c.execute( 'INSERT OR IGNORE INTO files_info VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? );', ( service_id, hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words ) )
|
||||
result = self._c.execute( 'SELECT 1 FROM files_info WHERE service_id = ? AND hash_id = ?;', ( service_id, hash_id ) ).fetchone()
|
||||
|
||||
if result is None:
|
||||
|
||||
self._c.execute( 'INSERT OR IGNORE INTO files_info VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? );', ( service_id, hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words ) )
|
||||
|
||||
delta_size += size
|
||||
|
||||
if mime in HC.MIMES_WITH_THUMBNAILS:
|
||||
|
||||
num_thumbnails += 1
|
||||
|
||||
|
||||
successful_hash_ids.add( hash_id )
|
||||
|
||||
|
||||
|
||||
if len( successful_hash_ids ) > 0:
|
||||
|
||||
splayed_successful_hash_ids = HydrusData.SplayListForDB( successful_hash_ids )
|
||||
|
||||
num_deleted = len( self._c.execute( 'SELECT 1 FROM deleted_files WHERE service_id = ? AND hash_id IN ' + splayed_successful_hash_ids + ';', ( service_id, ) ).fetchall() )
|
||||
|
||||
self._c.execute( 'DELETE FROM deleted_files WHERE service_id = ? AND hash_id IN ' + splayed_successful_hash_ids + ';', ( service_id, ) )
|
||||
|
||||
service_info_updates = []
|
||||
|
||||
result = self._c.execute( 'SELECT 1 FROM deleted_files WHERE service_id = ? AND hash_id = ?;', ( service_id, hash_id ) ).fetchone()
|
||||
|
||||
if result is not None:
|
||||
|
||||
self._c.execute( 'DELETE FROM deleted_files WHERE service_id = ? AND hash_id = ?;', ( service_id, hash_id ) )
|
||||
|
||||
service_info_updates.append( ( -1, service_id, HC.SERVICE_INFO_NUM_DELETED_FILES ) )
|
||||
|
||||
|
||||
if service_id != self._local_file_service_id:
|
||||
|
||||
if hash_id in self._inbox_hash_ids: service_info_updates.append( ( 1, service_id, HC.SERVICE_INFO_NUM_INBOX ) )
|
||||
|
||||
|
||||
service_info_updates.append( ( size, service_id, HC.SERVICE_INFO_TOTAL_SIZE ) )
|
||||
service_info_updates.append( ( 1, service_id, HC.SERVICE_INFO_NUM_FILES ) )
|
||||
if mime in HC.MIMES_WITH_THUMBNAILS: service_info_updates.append( ( 1, service_id, HC.SERVICE_INFO_NUM_THUMBNAILS ) )
|
||||
service_info_updates.append( ( -num_deleted, service_id, HC.SERVICE_INFO_NUM_DELETED_FILES ) )
|
||||
service_info_updates.append( ( delta_size, service_id, HC.SERVICE_INFO_TOTAL_SIZE ) )
|
||||
service_info_updates.append( ( len( successful_hash_ids ), service_id, HC.SERVICE_INFO_NUM_FILES ) )
|
||||
service_info_updates.append( ( num_thumbnails, service_id, HC.SERVICE_INFO_NUM_THUMBNAILS ) )
|
||||
service_info_updates.append( ( len( successful_hash_ids.intersection( self._inbox_hash_ids ) ), service_id, HC.SERVICE_INFO_NUM_INBOX ) )
|
||||
|
||||
self._c.executemany( 'UPDATE service_info SET info = info + ? WHERE service_id = ? AND info_type = ?;', service_info_updates )
|
||||
|
||||
self._c.execute( 'DELETE FROM file_transfers WHERE service_id = ? AND hash_id = ? ;', ( service_id, hash_id ) )
|
||||
self._c.execute( 'DELETE FROM file_transfers WHERE service_id = ? AND hash_id IN ' + HydrusData.SplayListForDB( successful_hash_ids ) + ';', ( service_id, ) )
|
||||
|
||||
if mime in HC.MIMES_WITH_THUMBNAILS: self._c.execute( 'DELETE FROM service_info WHERE service_id = ? AND info_type = ?;', ( service_id, HC.SERVICE_INFO_NUM_THUMBNAILS_LOCAL ) )
|
||||
if num_thumbnails > 0:
|
||||
|
||||
self._c.execute( 'DELETE FROM service_info WHERE service_id = ? AND info_type = ?;', ( service_id, HC.SERVICE_INFO_NUM_THUMBNAILS_LOCAL ) )
|
||||
|
||||
|
||||
self._UpdateAutocompleteTagCacheFromFiles( service_id, ( hash_id, ), 1 )
|
||||
self._UpdateAutocompleteTagCacheFromFiles( service_id, successful_hash_ids, 1 )
|
||||
|
||||
if service_id == self._local_file_service_id:
|
||||
|
||||
self._DeleteFiles( self._trash_service_id, successful_hash_ids )
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1091,25 +1115,6 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
self._c.execute( 'INSERT INTO services ( service_key, service_type, name, info ) VALUES ( ?, ?, ?, ? );', ( sqlite3.Binary( service_key ), service_type, name, info ) )
|
||||
|
||||
service_id = self._c.lastrowid
|
||||
|
||||
if service_type in ( HC.TAG_REPOSITORY, HC.LOCAL_TAG ):
|
||||
|
||||
file_service_ids = self._GetServiceIds( ( HC.FILE_REPOSITORY, HC.LOCAL_FILE, HC.COMBINED_FILE ) )
|
||||
|
||||
existing_tag_ids = self._c.execute( 'SELECT namespace_id, tag_id FROM existing_tags;' ).fetchall()
|
||||
|
||||
inserts = ( ( file_service_id, service_id, namespace_id, tag_id, 0, 0 ) for ( file_service_id, ( namespace_id, tag_id ) ) in itertools.product( file_service_ids, existing_tag_ids ) )
|
||||
|
||||
elif service_type == HC.FILE_REPOSITORY:
|
||||
|
||||
tag_service_ids = self._GetServiceIds( ( HC.TAG_REPOSITORY, HC.LOCAL_TAG, HC.COMBINED_TAG ) )
|
||||
|
||||
existing_tag_ids = self._c.execute( 'SELECT namespace_id, tag_id FROM existing_tags;' ).fetchall()
|
||||
|
||||
inserts = ( ( service_id, tag_service_id, namespace_id, tag_id, 0, 0 ) for ( tag_service_id, ( namespace_id, tag_id ) ) in itertools.product( tag_service_ids, existing_tag_ids ) )
|
||||
|
||||
|
||||
|
||||
def _AddThumbnails( self, thumbnails ):
|
||||
|
||||
|
@ -1202,7 +1207,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
HydrusGlobals.pubsub.pub( 'message', job_key )
|
||||
|
||||
info = self._c.execute( 'SELECT hash_id, mime FROM files_info WHERE service_id = ?;', ( self._local_file_service_id, ) ).fetchall()
|
||||
info = self._c.execute( 'SELECT hash_id, mime FROM files_info WHERE service_id IN ( ?, ? );', ( self._local_file_service_id, self._trash_service_id ) ).fetchall()
|
||||
|
||||
missing_count = 0
|
||||
deletee_hash_ids = []
|
||||
|
@ -1253,6 +1258,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
job_key.SetVariable( 'popup_text_1', prefix_string + 'deleting the incorrect records' )
|
||||
|
||||
self._DeleteFiles( self._local_file_service_id, deletee_hash_ids )
|
||||
self._DeleteFiles( self._trash_service_id, deletee_hash_ids )
|
||||
|
||||
final_text = 'done! '
|
||||
|
||||
|
@ -1407,7 +1413,8 @@ class DB( HydrusDB.HydrusDB ):
|
|||
self._c.execute( 'CREATE INDEX mappings_namespace_id_index ON mappings ( namespace_id );' )
|
||||
self._c.execute( 'CREATE INDEX mappings_tag_id_index ON mappings ( tag_id );' )
|
||||
self._c.execute( 'CREATE INDEX mappings_hash_id_index ON mappings ( hash_id );' )
|
||||
self._c.execute( 'CREATE INDEX mappings_status_index ON mappings ( status );' )
|
||||
self._c.execute( 'CREATE INDEX mappings_status_pending_index ON mappings ( status ) WHERE status = 1;' )
|
||||
self._c.execute( 'CREATE INDEX mappings_status_deleted_index ON mappings ( status ) WHERE status = 2;' )
|
||||
|
||||
self._c.execute( 'CREATE TABLE mapping_petitions ( service_id INTEGER REFERENCES services ON DELETE CASCADE, namespace_id INTEGER, tag_id INTEGER, hash_id INTEGER, reason_id INTEGER, PRIMARY KEY( service_id, namespace_id, tag_id, hash_id, reason_id ) );' )
|
||||
self._c.execute( 'CREATE INDEX mapping_petitions_hash_id_index ON mapping_petitions ( hash_id );' )
|
||||
|
@ -1497,6 +1504,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
init_service_info = []
|
||||
|
||||
init_service_info.append( ( CC.LOCAL_FILE_SERVICE_KEY, HC.LOCAL_FILE, CC.LOCAL_FILE_SERVICE_KEY ) )
|
||||
init_service_info.append( ( CC.TRASH_SERVICE_KEY, HC.LOCAL_FILE, CC.TRASH_SERVICE_KEY ) )
|
||||
init_service_info.append( ( CC.LOCAL_TAG_SERVICE_KEY, HC.LOCAL_TAG, CC.LOCAL_TAG_SERVICE_KEY ) )
|
||||
init_service_info.append( ( CC.COMBINED_FILE_SERVICE_KEY, HC.COMBINED_FILE, CC.COMBINED_FILE_SERVICE_KEY ) )
|
||||
init_service_info.append( ( CC.COMBINED_TAG_SERVICE_KEY, HC.COMBINED_TAG, CC.COMBINED_TAG_SERVICE_KEY ) )
|
||||
|
@ -1524,39 +1532,55 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
||||
|
||||
if service_id == self._local_file_service_id: self._ArchiveFiles( hash_ids )
|
||||
rows = self._c.execute( 'SELECT * FROM files_info WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, ) ).fetchall()
|
||||
|
||||
info = self._c.execute( 'SELECT size, mime FROM files_info WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, ) ).fetchall()
|
||||
# service_id, hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words
|
||||
hash_ids = { row[ 1 ] for row in rows }
|
||||
|
||||
total_size = sum( [ row[ 0 ] for row in info ] )
|
||||
num_files = len( info )
|
||||
num_thumbnails = len( [ 1 for row in info if row[ 1 ] in HC.MIMES_WITH_THUMBNAILS ] )
|
||||
|
||||
service_info_updates = []
|
||||
|
||||
service_info_updates.append( ( total_size, service_id, HC.SERVICE_INFO_TOTAL_SIZE ) )
|
||||
service_info_updates.append( ( num_files, service_id, HC.SERVICE_INFO_NUM_FILES ) )
|
||||
service_info_updates.append( ( num_thumbnails, service_id, HC.SERVICE_INFO_NUM_THUMBNAILS ) )
|
||||
service_info_updates.append( ( -num_files, service_id, HC.SERVICE_INFO_NUM_DELETED_FILES ) ) # - because we want to increment in the following query
|
||||
|
||||
self._c.executemany( 'UPDATE service_info SET info = info - ? WHERE service_id = ? AND info_type = ?;', service_info_updates )
|
||||
|
||||
self._c.execute( 'DELETE FROM service_info WHERE service_id = ? AND info_type = ' + str( HC.SERVICE_INFO_NUM_THUMBNAILS_LOCAL ) + ';', ( service_id, ) )
|
||||
|
||||
self._c.execute( 'DELETE FROM files_info WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, ) )
|
||||
self._c.execute( 'DELETE FROM file_petitions WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, ) )
|
||||
|
||||
invalid_hash_ids = { id for ( id, ) in self._c.execute( 'SELECT hash_id FROM deleted_files WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, ) ) }
|
||||
|
||||
actual_hash_ids_i_can_delete = set( hash_ids )
|
||||
|
||||
actual_hash_ids_i_can_delete.difference_update( invalid_hash_ids )
|
||||
|
||||
self._c.executemany( 'INSERT OR IGNORE INTO deleted_files ( service_id, hash_id ) VALUES ( ?, ? );', [ ( service_id, hash_id ) for hash_id in actual_hash_ids_i_can_delete ] )
|
||||
|
||||
self._UpdateAutocompleteTagCacheFromFiles( service_id, actual_hash_ids_i_can_delete, -1 )
|
||||
|
||||
self.pub_after_commit( 'notify_new_pending' )
|
||||
if len( hash_ids ) > 0:
|
||||
|
||||
total_size = sum( [ row[ 2 ] for row in rows ] )
|
||||
num_files = len( rows )
|
||||
num_thumbnails = len( [ 1 for row in rows if row[ 3 ] in HC.MIMES_WITH_THUMBNAILS ] )
|
||||
num_inbox = len( hash_ids.intersection( self._inbox_hash_ids ) )
|
||||
|
||||
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
||||
|
||||
service_info_updates = []
|
||||
|
||||
service_info_updates.append( ( -total_size, service_id, HC.SERVICE_INFO_TOTAL_SIZE ) )
|
||||
service_info_updates.append( ( -num_files, service_id, HC.SERVICE_INFO_NUM_FILES ) )
|
||||
service_info_updates.append( ( -num_thumbnails, service_id, HC.SERVICE_INFO_NUM_THUMBNAILS ) )
|
||||
service_info_updates.append( ( -num_inbox, service_id, HC.SERVICE_INFO_NUM_INBOX ) )
|
||||
service_info_updates.append( ( num_files, service_id, HC.SERVICE_INFO_NUM_DELETED_FILES ) )
|
||||
|
||||
self._c.executemany( 'UPDATE service_info SET info = info + ? WHERE service_id = ? AND info_type = ?;', service_info_updates )
|
||||
|
||||
self._c.execute( 'DELETE FROM service_info WHERE service_id = ? AND info_type = ' + str( HC.SERVICE_INFO_NUM_THUMBNAILS_LOCAL ) + ';', ( service_id, ) )
|
||||
|
||||
self._c.execute( 'DELETE FROM files_info WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, ) )
|
||||
self._c.execute( 'DELETE FROM file_petitions WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, ) )
|
||||
|
||||
self._c.executemany( 'INSERT OR IGNORE INTO deleted_files ( service_id, hash_id ) VALUES ( ?, ? );', [ ( service_id, hash_id ) for hash_id in hash_ids ] )
|
||||
|
||||
self._UpdateAutocompleteTagCacheFromFiles( service_id, hash_ids, -1 )
|
||||
|
||||
if service_id == self._local_file_service_id:
|
||||
|
||||
new_rows = [ ( hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words ) for ( service_id, hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words ) in rows ]
|
||||
|
||||
self._AddFiles( self._trash_service_id, new_rows )
|
||||
|
||||
|
||||
if service_id == self._trash_service_id:
|
||||
|
||||
self._ArchiveFiles( hash_ids )
|
||||
|
||||
self._DeletePhysicalFiles( hash_ids )
|
||||
|
||||
|
||||
self.pub_after_commit( 'notify_new_pending' )
|
||||
|
||||
|
||||
|
||||
def _DeleteHydrusSessionKey( self, service_key ):
|
||||
|
@ -1595,25 +1619,21 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
HydrusGlobals.pubsub.pub( 'message', job_key )
|
||||
|
||||
# careful of the .encode( 'hex' ) business here!
|
||||
|
||||
# files
|
||||
|
||||
deleted_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM deleted_files WHERE service_id = ?;', ( self._local_file_service_id, ) ) }
|
||||
deleted_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM deleted_files WHERE service_id = ?;', ( self._trash_service_id, ) ) }
|
||||
|
||||
pending_upload_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM file_transfers;', ) }
|
||||
potentially_pending_upload_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM file_transfers;', ) }
|
||||
|
||||
message_attachment_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM message_attachments;' ) }
|
||||
|
||||
deletee_hash_ids = ( deleted_hash_ids - pending_upload_hash_ids ) - message_attachment_hash_ids
|
||||
deletee_hash_ids = deleted_hash_ids.difference( potentially_pending_upload_hash_ids )
|
||||
|
||||
deletee_hashes = set( self._GetHashes( deletee_hash_ids ) )
|
||||
|
||||
local_files_hashes = ClientFiles.GetAllFileHashes()
|
||||
|
||||
job_key.SetVariable( 'popup_text_1', prefix + 'deleting orphan files' )
|
||||
|
||||
for hash in local_files_hashes & deletee_hashes:
|
||||
time.sleep( 1 )
|
||||
|
||||
for hash in ClientFiles.IterateAllFileHashes():
|
||||
|
||||
( i_paused, should_quit ) = job_key.WaitIfNeeded()
|
||||
|
||||
|
@ -1622,20 +1642,23 @@ class DB( HydrusDB.HydrusDB ):
|
|||
return
|
||||
|
||||
|
||||
try: path = ClientFiles.GetFilePath( hash )
|
||||
except HydrusExceptions.NotFoundException: continue
|
||||
|
||||
try:
|
||||
if hash in deletee_hashes:
|
||||
|
||||
try: os.chmod( path, stat.S_IWRITE | stat.S_IREAD )
|
||||
except: pass
|
||||
try: path = ClientFiles.GetFilePath( hash )
|
||||
except HydrusExceptions.NotFoundException: continue
|
||||
|
||||
os.remove( path )
|
||||
|
||||
except OSError:
|
||||
|
||||
print( 'In trying to delete the orphan ' + path + ', this error was encountered:' )
|
||||
print( traceback.format_exc() )
|
||||
try:
|
||||
|
||||
try: os.chmod( path, stat.S_IWRITE | stat.S_IREAD )
|
||||
except: pass
|
||||
|
||||
os.remove( path )
|
||||
|
||||
except OSError:
|
||||
|
||||
print( 'In trying to delete the orphan ' + path + ', this error was encountered:' )
|
||||
print( traceback.format_exc() )
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1728,6 +1751,65 @@ class DB( HydrusDB.HydrusDB ):
|
|||
self.pub_service_updates_after_commit( { service_key : [ HydrusData.ServiceUpdate( HC.SERVICE_UPDATE_DELETE_PENDING ) ] } )
|
||||
|
||||
|
||||
def _DeletePhysicalFiles( self, hash_ids ):
|
||||
|
||||
potentially_pending_upload_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM file_transfers;', ) }
|
||||
|
||||
deletable_file_hash_ids = hash_ids.difference( potentially_pending_upload_hash_ids )
|
||||
|
||||
if len( deletable_file_hash_ids ) > 0:
|
||||
|
||||
file_hashes = self._GetHashes( deletable_file_hash_ids )
|
||||
|
||||
for hash in file_hashes:
|
||||
|
||||
try: path = ClientFiles.GetFilePath( hash )
|
||||
except HydrusExceptions.NotFoundException: continue
|
||||
|
||||
try:
|
||||
|
||||
try: os.chmod( path, stat.S_IWRITE | stat.S_IREAD )
|
||||
except: pass
|
||||
|
||||
os.remove( path )
|
||||
|
||||
except OSError:
|
||||
|
||||
print( 'In trying to delete the orphan ' + path + ', this error was encountered:' )
|
||||
print( traceback.format_exc() )
|
||||
|
||||
|
||||
|
||||
|
||||
useful_thumbnail_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM files_info WHERE service_id != ? AND hash_id IN ' + HydrusData.SplayListForDB( hash_ids ) + ';', ( self._trash_service_id, ) ) }
|
||||
|
||||
deletable_thumbnail_hash_ids = hash_ids.difference( useful_thumbnail_hash_ids )
|
||||
|
||||
if len( deletable_thumbnail_hash_ids ) > 0:
|
||||
|
||||
thumbnail_hashes = self._GetHashes( deletable_thumbnail_hash_ids )
|
||||
|
||||
for hash in thumbnail_hashes:
|
||||
|
||||
path = ClientFiles.GetExpectedThumbnailPath( hash, True )
|
||||
resized_path = ClientFiles.GetExpectedThumbnailPath( hash, False )
|
||||
|
||||
try:
|
||||
|
||||
if os.path.exists( path ): os.remove( path )
|
||||
if os.path.exists( resized_path ): os.remove( resized_path )
|
||||
|
||||
except OSError:
|
||||
|
||||
print( 'In trying to delete the orphan ' + path + ' or ' + resized_path + ', this error was encountered:' )
|
||||
print( traceback.format_exc() )
|
||||
|
||||
|
||||
|
||||
self._c.execute( 'DELETE from perceptual_hashes WHERE hash_id IN ' + HydrusData.SplayListForDB( deletable_thumbnail_hash_ids ) + ';' )
|
||||
|
||||
|
||||
|
||||
def _DeleteServiceInfo( self ):
|
||||
|
||||
self._c.execute( 'DELETE FROM service_info;' )
|
||||
|
@ -1830,16 +1912,6 @@ class DB( HydrusDB.HydrusDB ):
|
|||
job_key.Finish()
|
||||
|
||||
|
||||
def _FattenAutocompleteCache( self ):
|
||||
|
||||
tag_services = self._GetServices( ( HC.TAG_REPOSITORY, HC.LOCAL_TAG, HC.COMBINED_TAG ) )
|
||||
file_services = self._GetServices( ( HC.FILE_REPOSITORY, HC.LOCAL_FILE, HC.COMBINED_FILE ) )
|
||||
|
||||
for ( tag_service, file_service ) in itertools.product( tag_services, file_services ): self._GetAutocompletePredicates( tag_service_key = tag_service.GetServiceKey(), file_service_key = file_service.GetServiceKey(), add_namespaceless = False )
|
||||
|
||||
self._c.execute( 'REPLACE INTO shutdown_timestamps ( shutdown_type, timestamp ) VALUES ( ?, ? );', ( CC.SHUTDOWN_TIMESTAMP_FATTEN_AC_CACHE, HydrusData.GetNow() ) )
|
||||
|
||||
|
||||
def _GetAutocompletePredicates( self, tag_service_key = CC.COMBINED_TAG_SERVICE_KEY, file_service_key = CC.COMBINED_FILE_SERVICE_KEY, tag = '', half_complete_tag = '', include_current = True, include_pending = True, add_namespaceless = False ):
|
||||
|
||||
tag_service_id = self._GetServiceId( tag_service_key )
|
||||
|
@ -2571,7 +2643,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if must_be_local or must_not_be_local:
|
||||
|
||||
if file_service_id == self._local_file_service_id:
|
||||
if file_service_type == HC.LOCAL_FILE:
|
||||
|
||||
if must_not_be_local: query_hash_ids = set()
|
||||
|
||||
|
@ -3138,7 +3210,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
current_update_weight = 0
|
||||
|
||||
pending_dict = HydrusData.BuildKeyToListDict( [ ( ( namespace_id, tag_id ), hash_id ) for ( namespace_id, tag_id, hash_id ) in self._c.execute( 'SELECT namespace_id, tag_id, hash_id FROM mappings INDEXED BY mappings_status_index WHERE service_id = ? AND status = ?;', ( service_id, HC.PENDING ) ) ] )
|
||||
pending_dict = HydrusData.BuildKeyToListDict( [ ( ( namespace_id, tag_id ), hash_id ) for ( namespace_id, tag_id, hash_id ) in self._c.execute( 'SELECT namespace_id, tag_id, hash_id FROM mappings WHERE service_id = ? AND status = ?;', ( service_id, HC.PENDING ) ) ] )
|
||||
|
||||
pending_chunks = []
|
||||
|
||||
|
@ -3376,13 +3448,34 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
service_type = service.GetServiceType()
|
||||
|
||||
if service_type == HC.LOCAL_FILE: info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_TOTAL_SIZE, HC.SERVICE_INFO_NUM_DELETED_FILES }
|
||||
elif service_type == HC.FILE_REPOSITORY: info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_TOTAL_SIZE, HC.SERVICE_INFO_NUM_DELETED_FILES, HC.SERVICE_INFO_NUM_THUMBNAILS, HC.SERVICE_INFO_NUM_THUMBNAILS_LOCAL }
|
||||
elif service_type == HC.LOCAL_TAG: info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_NUM_NAMESPACES, HC.SERVICE_INFO_NUM_TAGS, HC.SERVICE_INFO_NUM_MAPPINGS }
|
||||
elif service_type == HC.TAG_REPOSITORY: info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_NUM_NAMESPACES, HC.SERVICE_INFO_NUM_TAGS, HC.SERVICE_INFO_NUM_MAPPINGS, HC.SERVICE_INFO_NUM_DELETED_MAPPINGS }
|
||||
elif service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ): info_types = { HC.SERVICE_INFO_NUM_FILES }
|
||||
elif service_type == HC.LOCAL_BOORU: info_types = { HC.SERVICE_INFO_NUM_SHARES }
|
||||
else: info_types = set()
|
||||
if service_type == HC.LOCAL_FILE:
|
||||
|
||||
info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_TOTAL_SIZE, HC.SERVICE_INFO_NUM_DELETED_FILES }
|
||||
|
||||
elif service_type == HC.FILE_REPOSITORY:
|
||||
|
||||
info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_TOTAL_SIZE, HC.SERVICE_INFO_NUM_DELETED_FILES, HC.SERVICE_INFO_NUM_THUMBNAILS, HC.SERVICE_INFO_NUM_THUMBNAILS_LOCAL }
|
||||
|
||||
elif service_type == HC.LOCAL_TAG:
|
||||
|
||||
info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_NUM_NAMESPACES, HC.SERVICE_INFO_NUM_TAGS, HC.SERVICE_INFO_NUM_MAPPINGS }
|
||||
|
||||
elif service_type == HC.TAG_REPOSITORY:
|
||||
|
||||
info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_NUM_NAMESPACES, HC.SERVICE_INFO_NUM_TAGS, HC.SERVICE_INFO_NUM_MAPPINGS, HC.SERVICE_INFO_NUM_DELETED_MAPPINGS }
|
||||
|
||||
elif service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
|
||||
|
||||
info_types = { HC.SERVICE_INFO_NUM_FILES }
|
||||
|
||||
elif service_type == HC.LOCAL_BOORU:
|
||||
|
||||
info_types = { HC.SERVICE_INFO_NUM_SHARES }
|
||||
|
||||
else:
|
||||
|
||||
info_types = set()
|
||||
|
||||
|
||||
service_info = self._GetServiceInfoSpecific( service_id, service_type, info_types )
|
||||
|
||||
|
@ -3844,7 +3937,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
self._AddThumbnails( [ ( hash, thumbnail ) ] )
|
||||
|
||||
|
||||
self._AddFile( self._local_file_service_id, hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words )
|
||||
self._AddFiles( self._local_file_service_id, [ ( hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words ) ] )
|
||||
|
||||
content_update = HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_ADD, ( hash, size, mime, timestamp, width, height, duration, num_frames, num_words ) )
|
||||
|
||||
|
@ -3854,7 +3947,14 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
self._c.execute( 'INSERT OR IGNORE INTO local_hashes ( hash_id, md5, sha1, sha512 ) VALUES ( ?, ?, ?, ? );', ( hash_id, sqlite3.Binary( md5 ), sqlite3.Binary( sha1 ), sqlite3.Binary( sha512 ) ) )
|
||||
|
||||
if not archive: self._InboxFiles( ( hash_id, ) )
|
||||
if archive:
|
||||
|
||||
self._ArchiveFiles( ( hash_id, ) )
|
||||
|
||||
else:
|
||||
|
||||
self._InboxFiles( ( hash_id, ) )
|
||||
|
||||
|
||||
|
||||
if len( service_keys_to_tags ) > 0 and self._c.execute( 'SELECT 1 FROM files_info WHERE service_id = ? AND hash_id = ?;', ( self._local_file_service_id, hash_id ) ).fetchone() is not None:
|
||||
|
@ -3952,6 +4052,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
def _InitCaches( self ):
|
||||
|
||||
self._local_file_service_id = self._GetServiceId( CC.LOCAL_FILE_SERVICE_KEY )
|
||||
self._trash_service_id = self._GetServiceId( CC.TRASH_SERVICE_KEY )
|
||||
self._local_tag_service_id = self._GetServiceId( CC.LOCAL_TAG_SERVICE_KEY )
|
||||
self._combined_file_service_id = self._GetServiceId( CC.COMBINED_FILE_SERVICE_KEY )
|
||||
self._combined_tag_service_id = self._GetServiceId( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
|
@ -4019,7 +4120,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
hash_id = self._GetHashId( hash )
|
||||
|
||||
self._AddFile( service_id, hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words )
|
||||
self._AddFiles( service_id, [ ( hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words ) ] )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_PENDING:
|
||||
|
||||
|
@ -4075,6 +4176,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
if action == HC.CONTENT_UPDATE_ARCHIVE: self._ArchiveFiles( hash_ids )
|
||||
elif action == HC.CONTENT_UPDATE_INBOX: self._InboxFiles( hash_ids )
|
||||
elif action == HC.CONTENT_UPDATE_DELETE: self._DeleteFiles( service_id, hash_ids )
|
||||
elif action == HC.CONTENT_UPDATE_UNDELETE: self._UndeleteFiles( hash_ids )
|
||||
|
||||
|
||||
|
||||
|
@ -4453,6 +4555,8 @@ class DB( HydrusDB.HydrusDB ):
|
|||
try: service_id = self._GetServiceId( service_key )
|
||||
except HydrusExceptions.NotFoundException: continue
|
||||
|
||||
if service_id in self._service_cache: del self._service_cache[ service_id ]
|
||||
|
||||
service = self._GetService( service_id )
|
||||
|
||||
( service_key, service_type, name, info ) = service.ToTuple()
|
||||
|
@ -4879,6 +4983,44 @@ class DB( HydrusDB.HydrusDB ):
|
|||
self.pub_after_commit( 'notify_new_pending' )
|
||||
|
||||
|
||||
def _UndeleteFiles( self, hash_ids ):
|
||||
|
||||
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
||||
|
||||
rows = self._c.execute( 'SELECT * FROM files_info WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( self._trash_service_id, ) ).fetchall()
|
||||
|
||||
# service_id, hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words
|
||||
hash_ids = { row[ 1 ] for row in rows }
|
||||
|
||||
if len( hash_ids ) > 0:
|
||||
|
||||
total_size = sum( [ row[ 2 ] for row in rows ] )
|
||||
num_files = len( rows )
|
||||
num_thumbnails = len( [ 1 for row in rows if row[ 3 ] in HC.MIMES_WITH_THUMBNAILS ] )
|
||||
num_inbox = len( hash_ids.intersection( self._inbox_hash_ids ) )
|
||||
|
||||
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
||||
|
||||
service_info_updates = []
|
||||
|
||||
service_info_updates.append( ( -total_size, self._trash_service_id, HC.SERVICE_INFO_TOTAL_SIZE ) )
|
||||
service_info_updates.append( ( -num_files, self._trash_service_id, HC.SERVICE_INFO_NUM_FILES ) )
|
||||
service_info_updates.append( ( -num_thumbnails, self._trash_service_id, HC.SERVICE_INFO_NUM_THUMBNAILS ) )
|
||||
service_info_updates.append( ( -num_inbox, self._trash_service_id, HC.SERVICE_INFO_NUM_INBOX ) )
|
||||
|
||||
self._c.executemany( 'UPDATE service_info SET info = info + ? WHERE service_id = ? AND info_type = ?;', service_info_updates )
|
||||
|
||||
self._c.execute( 'DELETE FROM files_info WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( self._trash_service_id, ) )
|
||||
|
||||
self._UpdateAutocompleteTagCacheFromFiles( self._trash_service_id, hash_ids, -1 )
|
||||
|
||||
new_rows = [ ( hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words ) for ( service_id, hash_id, size, mime, timestamp, width, height, duration, num_frames, num_words ) in rows ]
|
||||
|
||||
self._AddFiles( self._local_file_service_id, new_rows )
|
||||
|
||||
|
||||
|
||||
|
||||
def _UpdateAutocompleteTagCacheFromFiles( self, file_service_id, hash_ids, direction ):
|
||||
|
||||
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
||||
|
@ -4898,18 +5040,6 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
HydrusGlobals.pubsub.pub( 'splash_set_text', 'updating db to v' + HydrusData.ToString( version + 1 ) )
|
||||
|
||||
if version == 114:
|
||||
|
||||
service_key = CC.LOCAL_BOORU_SERVICE_KEY
|
||||
service_type = HC.LOCAL_BOORU
|
||||
name = CC.LOCAL_BOORU_SERVICE_KEY
|
||||
info = {}
|
||||
|
||||
self._AddService( service_key, service_type, name, info )
|
||||
|
||||
self._c.execute( 'CREATE TABLE booru_shares ( service_id INTEGER REFERENCES services ( service_id ) ON DELETE CASCADE, share_key BLOB_BYTES, share TEXT_YAML, expiry INTEGER, used_monthly_data INTEGER, max_monthly_data INTEGER, ip_restriction TEXT, notes TEXT, PRIMARY KEY( service_id, share_key ) );' )
|
||||
|
||||
|
||||
if version == 115:
|
||||
|
||||
for path in ClientFiles.IterateAllFilePaths():
|
||||
|
@ -5475,6 +5605,27 @@ class DB( HydrusDB.HydrusDB ):
|
|||
self._c.execute( 'CREATE INDEX mappings_status_index ON mappings ( status );' )
|
||||
|
||||
|
||||
if version == 163:
|
||||
|
||||
self._c.execute( 'DROP INDEX mappings_status_index;' )
|
||||
|
||||
self._c.execute( 'CREATE INDEX mappings_status_pending_index ON mappings ( status ) WHERE status = 1;' )
|
||||
self._c.execute( 'CREATE INDEX mappings_status_deleted_index ON mappings ( status ) WHERE status = 2;' )
|
||||
|
||||
#
|
||||
|
||||
info = {}
|
||||
|
||||
self._AddService( CC.TRASH_SERVICE_KEY, HC.LOCAL_FILE, CC.TRASH_SERVICE_KEY, info )
|
||||
|
||||
self._trash_service_id = self._GetServiceId( CC.TRASH_SERVICE_KEY )
|
||||
self._local_file_service_id = self._GetServiceId( CC.LOCAL_FILE_SERVICE_KEY )
|
||||
|
||||
deleted_hash_ids = [ hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM deleted_files WHERE service_id = ?;', ( self._local_file_service_id, ) ) ]
|
||||
|
||||
self._c.executemany( 'INSERT OR IGNORE INTO deleted_files ( service_id, hash_id ) VALUES ( ?, ? );', ( ( self._trash_service_id, hash_id ) for hash_id in deleted_hash_ids ) )
|
||||
|
||||
|
||||
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
|
||||
|
||||
HydrusGlobals.is_db_updated = True
|
||||
|
@ -5976,8 +6127,6 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
self._c.execute( 'UPDATE services SET info = ? WHERE service_id = ?;', ( info, service_id ) )
|
||||
|
||||
if service_id in self._service_cache: del self._service_cache[ service_id ]
|
||||
|
||||
|
||||
def _Vacuum( self ):
|
||||
|
||||
|
@ -6031,7 +6180,6 @@ class DB( HydrusDB.HydrusDB ):
|
|||
elif action == 'delete_subscription': result = self._DeleteYAMLDump( YAML_DUMP_ID_SUBSCRIPTION, *args, **kwargs )
|
||||
elif action == 'export_folder': result = self._SetJSONDump( *args, **kwargs )
|
||||
elif action == 'export_mappings': result = self._ExportToTagArchive( *args, **kwargs )
|
||||
elif action == 'fatten_autocomplete_cache': result = self._FattenAutocompleteCache( *args, **kwargs )
|
||||
elif action == 'file_integrity': result = self._CheckFileIntegrity( *args, **kwargs )
|
||||
elif action == 'gui_session': result = self._SetJSONDump( *args, **kwargs )
|
||||
elif action == 'hydrus_session': result = self._AddHydrusSession( *args, **kwargs )
|
||||
|
|
|
@ -190,6 +190,7 @@ def DAEMONDownloadFiles():
|
|||
for service_key in service_keys:
|
||||
|
||||
if service_key == CC.LOCAL_FILE_SERVICE_KEY: break
|
||||
elif service_key == CC.TRASH_SERVICE_KEY: continue
|
||||
|
||||
try: file_repository = wx.GetApp().GetServicesManager().GetService( service_key )
|
||||
except HydrusExceptions.NotFoundException: continue
|
||||
|
@ -404,7 +405,7 @@ def DAEMONSynchroniseSubscriptions():
|
|||
|
||||
tags = query.split( ' ' )
|
||||
|
||||
all_args = ( ( booru, tags ), )
|
||||
all_args = ( ( booru_name, tags ), )
|
||||
|
||||
elif site_type == HC.SITE_TYPE_HENTAI_FOUNDRY:
|
||||
|
||||
|
|
|
@ -2010,7 +2010,7 @@ class UndoManager( object ):
|
|||
( data_type, action, row ) = content_update.ToTuple()
|
||||
|
||||
if data_type == HC.CONTENT_DATA_TYPE_FILES:
|
||||
if action in ( HC.CONTENT_UPDATE_ADD, HC.CONTENT_UPDATE_DELETE, HC.CONTENT_UPDATE_RESCIND_PETITION ): continue
|
||||
if action in ( HC.CONTENT_UPDATE_ADD, HC.CONTENT_UPDATE_DELETE, HC.CONTENT_UPDATE_UNDELETE, HC.CONTENT_UPDATE_RESCIND_PETITION ): continue
|
||||
elif data_type == HC.CONTENT_DATA_TYPE_MAPPINGS:
|
||||
|
||||
if action in ( HC.CONTENT_UPDATE_RESCIND_PETITION, HC.CONTENT_UPDATE_ADVANCED ): continue
|
||||
|
|
|
@ -209,14 +209,22 @@ class GalleryParser( object ):
|
|||
|
||||
class GalleryParserBooru( GalleryParser ):
|
||||
|
||||
def __init__( self, booru, tags ):
|
||||
def __init__( self, booru_name, tags ):
|
||||
|
||||
try:
|
||||
|
||||
self._booru = wx.GetApp().Read( 'remote_booru', booru_name )
|
||||
|
||||
except:
|
||||
|
||||
raise HydrusExceptions.NotFoundException( 'Attempted to find booru "' + booru_name + '", but it was missing from the database!' )
|
||||
|
||||
|
||||
self._booru = booru
|
||||
self._tags = tags
|
||||
|
||||
self._gallery_advance_num = None
|
||||
|
||||
( self._search_url, self._advance_by_page_num, self._search_separator, self._thumb_classname ) = booru.GetGalleryParsingInfo()
|
||||
( self._search_url, self._advance_by_page_num, self._search_separator, self._thumb_classname ) = self._booru.GetGalleryParsingInfo()
|
||||
|
||||
GalleryParser.__init__( self )
|
||||
|
||||
|
@ -296,55 +304,62 @@ class GalleryParserBooru( GalleryParser ):
|
|||
|
||||
image_url = None
|
||||
|
||||
if image_id is not None:
|
||||
try:
|
||||
|
||||
image = soup.find( id = image_id )
|
||||
|
||||
if image is None:
|
||||
if image_id is not None:
|
||||
|
||||
image_string = soup.find( text = re.compile( 'Save this file' ) )
|
||||
image = soup.find( id = image_id )
|
||||
|
||||
if image_string is None: image_string = soup.find( text = re.compile( 'Save this video' ) )
|
||||
|
||||
image = image_string.parent
|
||||
|
||||
image_url = image[ 'href' ]
|
||||
|
||||
else:
|
||||
|
||||
if image.name in ( 'img', 'video' ):
|
||||
if image is None:
|
||||
|
||||
image_url = image[ 'src' ]
|
||||
image_string = soup.find( text = re.compile( 'Save this file' ) )
|
||||
|
||||
if 'sample/sample-' in image_url:
|
||||
if image_string is None: image_string = soup.find( text = re.compile( 'Save this video' ) )
|
||||
|
||||
image = image_string.parent
|
||||
|
||||
image_url = image[ 'href' ]
|
||||
|
||||
else:
|
||||
|
||||
if image.name in ( 'img', 'video' ):
|
||||
|
||||
# danbooru resized image
|
||||
image_url = image[ 'src' ]
|
||||
|
||||
image = soup.find( id = 'image-resize-link' )
|
||||
if 'sample/sample-' in image_url:
|
||||
|
||||
# danbooru resized image
|
||||
|
||||
image = soup.find( id = 'image-resize-link' )
|
||||
|
||||
image_url = image[ 'href' ]
|
||||
|
||||
|
||||
elif image.name == 'a':
|
||||
|
||||
image_url = image[ 'href' ]
|
||||
|
||||
|
||||
elif image.name == 'a':
|
||||
|
||||
|
||||
if image_data is not None:
|
||||
|
||||
links = soup.find_all( 'a' )
|
||||
|
||||
for link in links:
|
||||
|
||||
image_url = image[ 'href' ]
|
||||
if link.string == image_data: image_url = link[ 'href' ]
|
||||
|
||||
|
||||
|
||||
|
||||
if image_data is not None:
|
||||
except Exception as e:
|
||||
|
||||
links = soup.find_all( 'a' )
|
||||
|
||||
for link in links:
|
||||
|
||||
if link.string == image_data: image_url = link[ 'href' ]
|
||||
|
||||
raise HydrusExceptions.NotFoundException( 'Could not parse a download link for ' + url_base + '!' + os.linesep + HydrusData.ToString( e ) )
|
||||
|
||||
|
||||
if image_url is None:
|
||||
|
||||
raise HydrusExceptions.NotFoundException( 'Could not find the image URL!' )
|
||||
raise HydrusExceptions.NotFoundException( 'Could not parse a download link for ' + url_base + '!' )
|
||||
|
||||
|
||||
image_url = urlparse.urljoin( url_base, image_url )
|
||||
|
|
|
@ -62,28 +62,6 @@ def GenerateExportFilename( media, terms ):
|
|||
|
||||
return filename
|
||||
|
||||
def GetAllFileHashes():
|
||||
|
||||
file_hashes = set()
|
||||
|
||||
for path in IterateAllFilePaths():
|
||||
|
||||
( base, filename ) = os.path.split( path )
|
||||
|
||||
result = filename.split( '.', 1 )
|
||||
|
||||
if len( result ) != 2: continue
|
||||
|
||||
( hash_encoded, ext ) = result
|
||||
|
||||
try: hash = hash_encoded.decode( 'hex' )
|
||||
except TypeError: continue
|
||||
|
||||
file_hashes.add( hash )
|
||||
|
||||
|
||||
return file_hashes
|
||||
|
||||
def GetAllPaths( raw_paths ):
|
||||
|
||||
file_paths = []
|
||||
|
@ -225,6 +203,24 @@ def GetExpectedServiceUpdatePackagePath( service_key, begin ):
|
|||
|
||||
return HC.CLIENT_UPDATES_DIR + os.path.sep + service_key.encode( 'hex' ) + '_' + str( begin ) + '_metadata.json'
|
||||
|
||||
def IterateAllFileHashes():
|
||||
|
||||
for path in IterateAllFilePaths():
|
||||
|
||||
( base, filename ) = os.path.split( path )
|
||||
|
||||
result = filename.split( '.', 1 )
|
||||
|
||||
if len( result ) != 2: continue
|
||||
|
||||
( hash_encoded, ext ) = result
|
||||
|
||||
try: hash = hash_encoded.decode( 'hex' )
|
||||
except TypeError: continue
|
||||
|
||||
yield hash
|
||||
|
||||
|
||||
def IterateAllFilePaths():
|
||||
|
||||
hex_chars = '0123456789abcdef'
|
||||
|
@ -236,6 +232,7 @@ def IterateAllFilePaths():
|
|||
next_paths = dircache.listdir( dir )
|
||||
|
||||
for path in next_paths: yield dir + os.path.sep + path
|
||||
|
||||
|
||||
def IterateAllThumbnailPaths():
|
||||
|
||||
|
@ -485,6 +482,8 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
|
||||
class LocationsManager( object ):
|
||||
|
||||
LOCAL_LOCATIONS = { CC.LOCAL_FILE_SERVICE_KEY, CC.TRASH_SERVICE_KEY }
|
||||
|
||||
def __init__( self, current, deleted, pending, petitioned ):
|
||||
|
||||
self._current = current
|
||||
|
@ -502,10 +501,16 @@ class LocationsManager( object ):
|
|||
def GetCDPP( self ): return ( self._current, self._deleted, self._pending, self._petitioned )
|
||||
|
||||
def GetCurrent( self ): return self._current
|
||||
def GetCurrentRemote( self ): return self._current - set( ( CC.LOCAL_FILE_SERVICE_KEY, ) )
|
||||
def GetCurrentRemote( self ):
|
||||
|
||||
return self._current - self.LOCAL_LOCATIONS
|
||||
|
||||
|
||||
def GetDeleted( self ): return self._deleted
|
||||
def GetDeletedRemote( self ): return self._deleted - set( ( CC.LOCAL_FILE_SERVICE_KEY, ) )
|
||||
def GetDeletedRemote( self ):
|
||||
|
||||
return self._deleted - self.LOCAL_LOCATIONS
|
||||
|
||||
|
||||
def GetFileRepositoryStrings( self ):
|
||||
|
||||
|
@ -548,14 +553,20 @@ class LocationsManager( object ):
|
|||
|
||||
|
||||
def GetPending( self ): return self._pending
|
||||
def GetPendingRemote( self ): return self._pending - set( ( CC.LOCAL_FILE_SERVICE_KEY, ) )
|
||||
def GetPendingRemote( self ):
|
||||
|
||||
return self._pending - self.LOCAL_LOCATIONS
|
||||
|
||||
|
||||
def GetPetitioned( self ): return self._petitioned
|
||||
def GetPetitionedRemote( self ): return self._petitioned - set( ( CC.LOCAL_FILE_SERVICE_KEY, ) )
|
||||
def GetPetitionedRemote( self ):
|
||||
|
||||
return self._petitioned - self.LOCAL_LOCATIONS
|
||||
|
||||
|
||||
def HasDownloading( self ): return CC.LOCAL_FILE_SERVICE_KEY in self._pending
|
||||
|
||||
def HasLocal( self ): return CC.LOCAL_FILE_SERVICE_KEY in self._current
|
||||
def HasLocal( self ): return len( self._current.union( self.LOCAL_LOCATIONS ) ) > 0
|
||||
|
||||
def ProcessContentUpdate( self, service_key, content_update ):
|
||||
|
||||
|
@ -568,6 +579,11 @@ class LocationsManager( object ):
|
|||
self._deleted.discard( service_key )
|
||||
self._pending.discard( service_key )
|
||||
|
||||
if service_key == CC.LOCAL_FILE_SERVICE_KEY:
|
||||
|
||||
self._current.discard( CC.TRASH_SERVICE_KEY )
|
||||
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_DELETE:
|
||||
|
||||
self._deleted.add( service_key )
|
||||
|
@ -575,6 +591,17 @@ class LocationsManager( object ):
|
|||
self._current.discard( service_key )
|
||||
self._petitioned.discard( service_key )
|
||||
|
||||
if service_key == CC.LOCAL_FILE_SERVICE_KEY:
|
||||
|
||||
self._current.add( CC.TRASH_SERVICE_KEY )
|
||||
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_UNDELETE:
|
||||
|
||||
self._current.discard( CC.TRASH_SERVICE_KEY )
|
||||
|
||||
self._current.add( CC.LOCAL_FILE_SERVICE_KEY )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_PENDING:
|
||||
|
||||
if service_key not in self._current: self._pending.add( service_key )
|
||||
|
|
|
@ -662,6 +662,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
|||
|
||||
menu = wx.Menu()
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY ), p( '&New Local Search' ), p( 'Open a new search tab for your files' ) )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_page_query', CC.TRASH_SERVICE_KEY ), p( '&New Trash Search' ), p( 'Open a new search tab for your recently deleted files' ) )
|
||||
for service in file_repositories: menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'new_page_query', service.GetServiceKey() ), p( 'New ' + service.GetName() + ' Search' ), p( 'Open a new search tab for ' + service.GetName() + '.' ) )
|
||||
if len( petition_resolve_tag_services ) > 0 or len( petition_resolve_file_services ) > 0:
|
||||
|
||||
|
@ -721,7 +722,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
|||
submenu = wx.Menu()
|
||||
|
||||
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'vacuum_db' ), p( '&Vacuum' ), p( 'Rebuild the Database.' ) )
|
||||
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_orphans' ), p( '&Delete Orphan Files' ), p( 'Go through the client\'s file store, deleting any files that are no longer needed.' ) )
|
||||
#submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_orphans' ), p( '&Delete Orphan Files' ), p( 'Go through the client\'s file store, deleting any files that are no longer needed.' ) )
|
||||
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_service_info' ), p( '&Clear Service Info Cache' ), p( 'Delete all cache service info, in case it has become desynchronised.' ) )
|
||||
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'regenerate_thumbnails' ), p( '&Regenerate All Thumbnails' ), p( 'Delete all thumbnails and regenerate from original files.' ) )
|
||||
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'file_integrity' ), p( '&Check File Integrity' ), p( 'Review and fix all local file records.' ) )
|
||||
|
@ -1183,7 +1184,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
|||
|
||||
booru = dlg.GetBooru()
|
||||
|
||||
self._NewPageImportGallery( HC.SITE_TYPE_BOORU, booru )
|
||||
self._NewPageImportGallery( HC.SITE_TYPE_BOORU, booru.GetName() )
|
||||
|
||||
|
||||
|
||||
|
@ -2489,6 +2490,7 @@ class FrameReviewServices( ClientGUICommon.Frame ):
|
|||
if service_type in ( HC.LOCAL_FILE, HC.FILE_REPOSITORY ):
|
||||
|
||||
self._info_panel.AddF( self._files_text, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self._info_panel.AddF( self._deleted_files_text, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
if service_type == HC.FILE_REPOSITORY:
|
||||
|
@ -2756,10 +2758,11 @@ class FrameReviewServices( ClientGUICommon.Frame ):
|
|||
|
||||
num_files = service_info[ HC.SERVICE_INFO_NUM_FILES ]
|
||||
total_size = service_info[ HC.SERVICE_INFO_TOTAL_SIZE ]
|
||||
num_deleted_files = service_info[ HC.SERVICE_INFO_NUM_DELETED_FILES ]
|
||||
|
||||
self._files_text.SetLabel( HydrusData.ConvertIntToPrettyString( num_files ) + ' files, totalling ' + HydrusData.ConvertIntToBytes( total_size ) )
|
||||
|
||||
num_deleted_files = service_info[ HC.SERVICE_INFO_NUM_DELETED_FILES ]
|
||||
|
||||
self._deleted_files_text.SetLabel( HydrusData.ConvertIntToPrettyString( num_deleted_files ) + ' deleted files' )
|
||||
|
||||
if service_type == HC.FILE_REPOSITORY:
|
||||
|
|
|
@ -560,11 +560,38 @@ class Canvas( object ):
|
|||
HydrusGlobals.pubsub.pub( 'clipboard', 'text', path )
|
||||
|
||||
|
||||
def _Delete( self ):
|
||||
def _Delete( self, service_key = None ):
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'Delete this file from the database?' ) as dlg:
|
||||
if service_key is None:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES: wx.GetApp().Write( 'content_updates', { CC.LOCAL_FILE_SERVICE_KEY : [ HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, ( self._current_display_media.GetHash(), ) ) ] } )
|
||||
locations_manager = self._current_display_media.GetLocationsManager()
|
||||
|
||||
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
service_key = CC.LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
elif CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
service_key = CC.TRASH_SERVICE_KEY
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
if service_key == CC.LOCAL_FILE_SERVICE_KEY:
|
||||
|
||||
text = 'Send this file to the trash?'
|
||||
|
||||
elif service_key == CC.TRASH_SERVICE_KEY:
|
||||
|
||||
text = 'Permanently delete this file?'
|
||||
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES: wx.GetApp().Write( 'content_updates', { service_key : [ HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, ( self._current_display_media.GetHash(), ) ) ] } )
|
||||
|
||||
|
||||
self.SetFocus() # annoying bug because of the modal dialog
|
||||
|
@ -696,6 +723,16 @@ class Canvas( object ):
|
|||
if new_position != self._media_container.GetPosition(): self._media_container.SetPosition( new_position )
|
||||
|
||||
|
||||
def _Undelete( self ):
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'Undelete this file?' ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES: wx.GetApp().Write( 'content_updates', { CC.TRASH_SERVICE_KEY : [ HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_UNDELETE, ( self._current_display_media.GetHash(), ) ) ] } )
|
||||
|
||||
|
||||
self.SetFocus() # annoying bug because of the modal dialog
|
||||
|
||||
|
||||
def _ZoomIn( self ):
|
||||
|
||||
if self._current_display_media is not None:
|
||||
|
@ -1005,9 +1042,26 @@ class CanvasWithDetails( Canvas ):
|
|||
|
||||
icons_to_show = []
|
||||
|
||||
if CC.TRASH_SERVICE_KEY in self._current_media.GetLocationsManager().GetCurrent():
|
||||
|
||||
icons_to_show.append( CC.GlobalBMPs.trash )
|
||||
|
||||
|
||||
if self._current_media.HasInbox():
|
||||
|
||||
dc.DrawBitmap( CC.GlobalBMPs.inbox, client_width - 18, 2 )
|
||||
icons_to_show.append( CC.GlobalBMPs.inbox )
|
||||
|
||||
|
||||
if len( icons_to_show ) > 0:
|
||||
|
||||
icon_x = 0
|
||||
|
||||
for icon in icons_to_show:
|
||||
|
||||
dc.DrawBitmap( icon, client_width + icon_x - 18, 2 )
|
||||
|
||||
icon_x -= 18
|
||||
|
||||
|
||||
current_y += 18
|
||||
|
||||
|
@ -1135,11 +1189,12 @@ class CanvasPanel( Canvas, wx.Window ):
|
|||
elif command == 'copy_hash': self._CopyHashToClipboard()
|
||||
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
|
||||
elif command == 'copy_path': self._CopyPathToClipboard()
|
||||
elif command == 'delete': self._Delete()
|
||||
elif command == 'delete': self._Delete( data )
|
||||
elif command == 'inbox': self._Inbox()
|
||||
elif command == 'manage_ratings': self._ManageRatings()
|
||||
elif command == 'manage_tags': wx.CallAfter( self._ManageTags )
|
||||
elif command == 'open_externally': self._OpenExternally()
|
||||
elif command == 'undelete': self._Undelete()
|
||||
else: event.Skip()
|
||||
|
||||
|
||||
|
@ -1151,6 +1206,8 @@ class CanvasPanel( Canvas, wx.Window ):
|
|||
|
||||
services = wx.GetApp().GetServicesManager().GetServices()
|
||||
|
||||
locations_manager = self._current_display_media.GetLocationsManager()
|
||||
|
||||
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
|
||||
|
@ -1182,11 +1239,20 @@ class CanvasPanel( Canvas, wx.Window ):
|
|||
|
||||
if self._current_display_media.HasInbox(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'archive' ), '&archive' )
|
||||
if self._current_display_media.HasArchive(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'inbox' ), 'return to &inbox' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
|
||||
|
||||
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
|
||||
|
||||
elif CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.TRASH_SERVICE_KEY ), '&delete from trash now' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'undelete' ), '&undelete' )
|
||||
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally', CC.LOCAL_FILE_SERVICE_KEY ), '&open externally' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally' ), '&open externally' )
|
||||
|
||||
share_menu = wx.Menu()
|
||||
|
||||
|
@ -2028,7 +2094,7 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaListNavigable ):
|
|||
elif command == 'copy_hash': self._CopyHashToClipboard()
|
||||
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
|
||||
elif command == 'copy_path': self._CopyPathToClipboard()
|
||||
elif command == 'delete': self._Delete()
|
||||
elif command == 'delete': self._Delete( data )
|
||||
elif command == 'fullscreen_switch': self._FullscreenSwitch()
|
||||
elif command == 'first': self._ShowFirst()
|
||||
elif command == 'last': self._ShowLast()
|
||||
|
@ -2052,6 +2118,7 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaListNavigable ):
|
|||
elif command == 'remove': self._Remove()
|
||||
elif command == 'slideshow': wx.CallAfter( self._StartSlideshow, data )
|
||||
elif command == 'slideshow_pause_play': wx.CallAfter( self._PausePlaySlideshow )
|
||||
elif command == 'undelete': self._Undelete()
|
||||
elif command == 'zoom_in': self._ZoomIn()
|
||||
elif command == 'zoom_out': self._ZoomOut()
|
||||
elif command == 'zoom_switch': self._ZoomSwitch()
|
||||
|
@ -2088,6 +2155,8 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaListNavigable ):
|
|||
|
||||
self._last_drag_coordinates = None # to stop successive right-click drag warp bug
|
||||
|
||||
locations_manager = self._current_display_media.GetLocationsManager()
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
menu.Append( CC.ID_NULL, self._current_display_media.GetPrettyInfo() )
|
||||
|
@ -2137,12 +2206,22 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaListNavigable ):
|
|||
|
||||
if self._current_display_media.HasInbox(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'archive' ), '&archive' )
|
||||
if self._current_display_media.HasArchive(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'inbox' ), 'return to &inbox' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'remove', CC.LOCAL_FILE_SERVICE_KEY ), '&remove' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'remove' ), '&remove' )
|
||||
|
||||
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
|
||||
|
||||
elif CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.TRASH_SERVICE_KEY ), '&delete from trash now' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'undelete' ), '&undelete' )
|
||||
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally', CC.LOCAL_FILE_SERVICE_KEY ), '&open externally' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally' ), '&open externally' )
|
||||
|
||||
share_menu = wx.Menu()
|
||||
|
||||
|
@ -2423,7 +2502,7 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaListNavigable
|
|||
elif command == 'copy_hash': self._CopyHashToClipboard()
|
||||
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
|
||||
elif command == 'copy_path': self._CopyPathToClipboard()
|
||||
elif command == 'delete': self._Delete()
|
||||
elif command == 'delete': self._Delete( data )
|
||||
elif command == 'fullscreen_switch': self._FullscreenSwitch()
|
||||
elif command == 'first': self._ShowFirst()
|
||||
elif command == 'last': self._ShowLast()
|
||||
|
@ -2436,6 +2515,7 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaListNavigable
|
|||
elif command == 'manage_tags': wx.CallAfter( self._ManageTags )
|
||||
elif command == 'open_externally': self._OpenExternally()
|
||||
elif command == 'remove': self._Remove()
|
||||
elif command == 'undelete': self._Undelete()
|
||||
elif command == 'zoom_in': self._ZoomIn()
|
||||
elif command == 'zoom_out': self._ZoomOut()
|
||||
elif command == 'zoom_switch': self._ZoomSwitch()
|
||||
|
@ -2470,6 +2550,8 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaListNavigable
|
|||
|
||||
i_can_post_ratings = len( local_ratings_services ) > 0
|
||||
|
||||
locations_manager = self._current_display_media.GetLocationsManager()
|
||||
|
||||
#
|
||||
|
||||
self._last_drag_coordinates = None # to stop successive right-click drag warp bug
|
||||
|
@ -2523,12 +2605,22 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaListNavigable
|
|||
|
||||
if self._current_display_media.HasInbox(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'archive' ), '&archive' )
|
||||
if self._current_display_media.HasArchive(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'inbox' ), 'return to &inbox' )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'remove' ), '&remove' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
|
||||
|
||||
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
|
||||
|
||||
elif CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.TRASH_SERVICE_KEY ), '&delete from trash now' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'undelete' ), '&undelete' )
|
||||
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally', CC.LOCAL_FILE_SERVICE_KEY ), '&open externally' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally' ), '&open externally' )
|
||||
|
||||
share_menu = wx.Menu()
|
||||
|
||||
|
|
|
@ -397,9 +397,7 @@ class AutoCompleteDropdown( wx.Panel ):
|
|||
if event.GetWheelRotation() > 0: command_type = wx.wxEVT_SCROLLWIN_LINEUP
|
||||
else: command_type = wx.wxEVT_SCROLLWIN_LINEDOWN
|
||||
|
||||
scroll_event = wx.ScrollEvent( command_type )
|
||||
|
||||
self._dropdown_list.EventScroll( scroll_event )
|
||||
wx.PostEvent( self, wx.ScrollWinEvent( command_type ) )
|
||||
|
||||
|
||||
|
||||
|
@ -529,6 +527,7 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
services = []
|
||||
services.append( services_manager.GetService( CC.COMBINED_FILE_SERVICE_KEY ) )
|
||||
services.append( services_manager.GetService( CC.LOCAL_FILE_SERVICE_KEY ) )
|
||||
services.append( services_manager.GetService( CC.TRASH_SERVICE_KEY ) )
|
||||
services.extend( services_manager.GetServices( ( HC.FILE_REPOSITORY, ) ) )
|
||||
|
||||
menu = wx.Menu()
|
||||
|
@ -1944,18 +1943,19 @@ class ListBox( wx.ScrolledWindow ):
|
|||
|
||||
self._background_colour = wx.Colour( 255, 255, 255 )
|
||||
|
||||
self._current_y_offset = 0
|
||||
self._drawn_up_to = 0
|
||||
|
||||
self._ordered_strings = []
|
||||
self._strings_to_terms = {}
|
||||
|
||||
self._canvas_bmp = wx.EmptyBitmap( 0, 0, 24 )
|
||||
self._client_bmp = wx.EmptyBitmap( 0, 0, 24 )
|
||||
|
||||
self._current_selected_index = None
|
||||
self._current_selected_term = None
|
||||
|
||||
dc = self._GetScrolledDC()
|
||||
self._last_virtual_size = None
|
||||
self._last_view_start = None
|
||||
self._dirty = True
|
||||
|
||||
dc = wx.MemoryDC( self._client_bmp )
|
||||
|
||||
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
|
||||
|
||||
|
@ -1979,103 +1979,12 @@ class ListBox( wx.ScrolledWindow ):
|
|||
self.Bind( wx.EVT_CHAR_HOOK, self.EventKeyDown )
|
||||
|
||||
self.Bind( wx.EVT_MENU, self.EventMenu )
|
||||
self.Bind( wx.EVT_SCROLLWIN, self.EventScroll )
|
||||
|
||||
|
||||
def __len__( self ): return len( self._ordered_strings )
|
||||
|
||||
def _Activate( self, s, term ): pass
|
||||
|
||||
def _DrawText( self, index ):
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
dc = self._GetScrolledDC()
|
||||
|
||||
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
|
||||
|
||||
i = 0
|
||||
|
||||
dc.SetBackground( wx.Brush( self._background_colour ) )
|
||||
|
||||
i = index
|
||||
text = self._ordered_strings[ i ]
|
||||
|
||||
( r, g, b ) = self._GetTextColour( text )
|
||||
|
||||
text_colour = wx.Colour( r, g, b )
|
||||
|
||||
if i == self._current_selected_index:
|
||||
|
||||
dc.SetBrush( wx.Brush( text_colour ) )
|
||||
|
||||
text_colour = wx.WHITE
|
||||
|
||||
|
||||
dc.SetPen( wx.TRANSPARENT_PEN )
|
||||
|
||||
dc.DrawRectangle( 0, i * self._text_y, my_width, self._text_y )
|
||||
|
||||
dc.SetTextForeground( text_colour )
|
||||
|
||||
( x, y ) = ( 3, i * self._text_y )
|
||||
|
||||
dc.DrawText( text, x, y )
|
||||
|
||||
|
||||
def _DrawTexts( self ):
|
||||
|
||||
( start_x, start_y ) = self.GetViewStart()
|
||||
|
||||
( xUnit, yUnit ) = self.GetScrollPixelsPerUnit()
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
draw_up_to = ( ( start_y + self._current_y_offset ) * yUnit ) + my_height
|
||||
|
||||
if draw_up_to > self._drawn_up_to:
|
||||
|
||||
dc = self._GetScrolledDC()
|
||||
|
||||
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
|
||||
|
||||
i = 0
|
||||
|
||||
dc.SetBackground( wx.Brush( self._background_colour ) )
|
||||
|
||||
if self._drawn_up_to == 0: dc.Clear()
|
||||
|
||||
for ( i, text ) in enumerate( self._ordered_strings ):
|
||||
|
||||
if i * self._text_y < self._drawn_up_to: continue
|
||||
if i * self._text_y > draw_up_to: break
|
||||
|
||||
( r, g, b ) = self._GetTextColour( text )
|
||||
|
||||
text_colour = wx.Colour( r, g, b )
|
||||
|
||||
if self._current_selected_index is not None and i == self._current_selected_index:
|
||||
|
||||
dc.SetBrush( wx.Brush( text_colour ) )
|
||||
|
||||
dc.SetPen( wx.TRANSPARENT_PEN )
|
||||
|
||||
dc.DrawRectangle( 0, i * self._text_y, my_width, self._text_y )
|
||||
|
||||
text_colour = wx.WHITE
|
||||
|
||||
|
||||
dc.SetTextForeground( text_colour )
|
||||
|
||||
( x, y ) = ( 3, i * self._text_y )
|
||||
|
||||
dc.DrawText( text, x, y )
|
||||
|
||||
|
||||
self._drawn_up_to = draw_up_to
|
||||
|
||||
|
||||
|
||||
def _GetIndexUnderMouse( self, mouse_event ):
|
||||
|
||||
( xUnit, yUnit ) = self.GetScrollPixelsPerUnit()
|
||||
|
@ -2093,17 +2002,66 @@ class ListBox( wx.ScrolledWindow ):
|
|||
return row_index
|
||||
|
||||
|
||||
def _GetScrolledDC( self ):
|
||||
|
||||
cdc = wx.ClientDC( self )
|
||||
|
||||
self.DoPrepareDC( cdc ) # because this is a scrolled window
|
||||
|
||||
return wx.BufferedDC( cdc, self._canvas_bmp, wx.BUFFER_VIRTUAL_AREA )
|
||||
|
||||
|
||||
def _GetTextColour( self, text ): return ( 0, 111, 250 )
|
||||
|
||||
def _Redraw( self, dc ):
|
||||
|
||||
( xUnit, yUnit ) = self.GetScrollPixelsPerUnit()
|
||||
|
||||
( x_scroll, y_scroll ) = self.GetViewStart()
|
||||
|
||||
self._last_view_start = self.GetViewStart()
|
||||
|
||||
y_offset = y_scroll * yUnit
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
first_visible_index = y_offset / self._text_y
|
||||
|
||||
last_visible_index = ( y_offset + my_height ) / self._text_y
|
||||
|
||||
if ( y_offset + my_height ) % self._text_y != 0:
|
||||
|
||||
last_visible_index += 1
|
||||
|
||||
|
||||
last_visible_index = min( last_visible_index, len( self._ordered_strings ) - 1 )
|
||||
|
||||
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
|
||||
|
||||
dc.SetBackground( wx.Brush( self._background_colour ) )
|
||||
|
||||
dc.Clear()
|
||||
|
||||
for ( i, current_index ) in enumerate( range( first_visible_index, last_visible_index + 1 ) ):
|
||||
|
||||
text = self._ordered_strings[ current_index ]
|
||||
|
||||
( r, g, b ) = self._GetTextColour( text )
|
||||
|
||||
text_colour = wx.Colour( r, g, b )
|
||||
|
||||
if self._current_selected_index is not None and current_index == self._current_selected_index:
|
||||
|
||||
dc.SetBrush( wx.Brush( text_colour ) )
|
||||
|
||||
dc.SetPen( wx.TRANSPARENT_PEN )
|
||||
|
||||
dc.DrawRectangle( 0, i * self._text_y, my_width, self._text_y )
|
||||
|
||||
text_colour = wx.WHITE
|
||||
|
||||
|
||||
dc.SetTextForeground( text_colour )
|
||||
|
||||
( x, y ) = ( 3, i * self._text_y )
|
||||
|
||||
dc.DrawText( text, x, y )
|
||||
|
||||
|
||||
self._dirty = False
|
||||
|
||||
|
||||
def _Select( self, index ):
|
||||
|
||||
old_index = self._current_selected_index
|
||||
|
@ -2116,15 +2074,14 @@ class ListBox( wx.ScrolledWindow ):
|
|||
|
||||
self._current_selected_index = index
|
||||
|
||||
if old_index is not None: self._DrawText( old_index )
|
||||
|
||||
if self._current_selected_index is None: self._current_selected_term = None
|
||||
if self._current_selected_index is None:
|
||||
|
||||
self._current_selected_term = None
|
||||
|
||||
else:
|
||||
|
||||
self._current_selected_term = self._strings_to_terms[ self._ordered_strings[ self._current_selected_index ] ]
|
||||
|
||||
self._DrawText( self._current_selected_index )
|
||||
|
||||
# scroll to index, if needed
|
||||
|
||||
y = self._text_y * self._current_selected_index
|
||||
|
@ -2153,11 +2110,18 @@ class ListBox( wx.ScrolledWindow ):
|
|||
|
||||
|
||||
|
||||
self._SetDirty()
|
||||
|
||||
|
||||
def _SetDirty( self ):
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.Refresh()
|
||||
|
||||
|
||||
def _TextsHaveChanged( self ):
|
||||
|
||||
self._drawn_up_to = 0
|
||||
|
||||
self._current_selected_index = None
|
||||
|
||||
if self._current_selected_term is not None:
|
||||
|
@ -2175,12 +2139,20 @@ class ListBox( wx.ScrolledWindow ):
|
|||
if self._current_selected_index is None: self._current_selected_term = None
|
||||
|
||||
|
||||
total_height = self._text_y * len( self._ordered_strings )
|
||||
( my_x, my_y ) = self.GetClientSize()
|
||||
|
||||
( my_x, my_y ) = self._canvas_bmp.GetSize()
|
||||
total_height = max( self._text_y * len( self._ordered_strings ), my_y )
|
||||
|
||||
if my_y != total_height: wx.PostEvent( self, wx.SizeEvent() )
|
||||
else: self._DrawTexts()
|
||||
( virtual_x, virtual_y ) = self.GetVirtualSize()
|
||||
|
||||
if total_height != virtual_y:
|
||||
|
||||
wx.PostEvent( self, wx.SizeEvent() )
|
||||
|
||||
else:
|
||||
|
||||
self._SetDirty()
|
||||
|
||||
|
||||
|
||||
def EventDClick( self, event ):
|
||||
|
@ -2293,63 +2265,37 @@ class ListBox( wx.ScrolledWindow ):
|
|||
event.Skip()
|
||||
|
||||
|
||||
def EventPaint( self, event ): wx.BufferedPaintDC( self, self._canvas_bmp, wx.BUFFER_VIRTUAL_AREA )
|
||||
def EventPaint( self, event ):
|
||||
|
||||
dc = wx.BufferedPaintDC( self, self._client_bmp )
|
||||
|
||||
if self._dirty or self._last_view_start != self.GetViewStart():
|
||||
|
||||
self._Redraw( dc )
|
||||
|
||||
|
||||
|
||||
def EventResize( self, event ):
|
||||
|
||||
( client_x, client_y ) = self.GetClientSize()
|
||||
( my_x, my_y ) = self.GetClientSize()
|
||||
|
||||
( my_x, my_y ) = self._canvas_bmp.GetSize()
|
||||
self._num_rows_per_page = my_y / self._text_y
|
||||
|
||||
self._num_rows_per_page = client_y / self._text_y
|
||||
ideal_virtual_size = ( my_x, max( self._text_y * len( self._ordered_strings ), my_y ) )
|
||||
|
||||
total_height = self._text_y * len( self._ordered_strings )
|
||||
|
||||
if my_x != client_x or my_y != total_height:
|
||||
if ideal_virtual_size != self._last_virtual_size:
|
||||
|
||||
new_y = max( client_y, total_height )
|
||||
self.SetVirtualSize( ideal_virtual_size )
|
||||
|
||||
self.SetVirtualSize( ( client_x, new_y ) )
|
||||
self._last_virtual_size = ideal_virtual_size
|
||||
|
||||
self._canvas_bmp = wx.EmptyBitmap( client_x, new_y, 24 )
|
||||
if self._client_bmp.GetSize() != ( my_x, my_y ):
|
||||
|
||||
self._client_bmp = wx.EmptyBitmap( my_x, my_y, 24 )
|
||||
|
||||
|
||||
self._drawn_up_to = 0
|
||||
self._SetDirty()
|
||||
|
||||
self._DrawTexts()
|
||||
|
||||
|
||||
|
||||
def EventScroll( self, event ):
|
||||
|
||||
# it seems that some scroll events happen after the viewstart has changed, some happen before
|
||||
# so I have to keep track of a manual current_y_start
|
||||
|
||||
( start_x, start_y ) = self.GetViewStart()
|
||||
|
||||
( my_virtual_width, my_virtual_height ) = self.GetVirtualSize()
|
||||
|
||||
( my_width, my_height ) = self.GetClientSize()
|
||||
|
||||
( xUnit, yUnit ) = self.GetScrollPixelsPerUnit()
|
||||
|
||||
page_of_y_units = my_height / yUnit
|
||||
|
||||
event_type = event.GetEventType()
|
||||
|
||||
if event_type == wx.wxEVT_SCROLLWIN_LINEUP: self._current_y_offset = -1
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_LINEDOWN: self._current_y_offset = 1
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_THUMBTRACK: self._current_y_offset = 0
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_THUMBRELEASE: self._current_y_offset = 0
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_PAGEUP: self._current_y_offset = - page_of_y_units
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_PAGEDOWN: self._current_y_offset = page_of_y_units
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_TOP: self._current_y_offset = - start_y
|
||||
elif event_type == wx.wxEVT_SCROLLWIN_BOTTOM: self._current_y_offset = ( my_virtual_height / yUnit ) - start_y
|
||||
|
||||
self._DrawTexts()
|
||||
|
||||
self._current_y_offset = 0
|
||||
|
||||
event.Skip()
|
||||
|
||||
|
||||
def GetClientData( self, s = None ):
|
||||
|
|
|
@ -1247,11 +1247,12 @@ class DialogInputFileSystemPredicates( Dialog ):
|
|||
|
||||
services_manager = wx.GetApp().GetServicesManager()
|
||||
|
||||
ratings_like = services_manager.GetServices( ( HC.LOCAL_RATING_LIKE, ) )
|
||||
ratings_numerical = services_manager.GetServices( ( HC.LOCAL_RATING_NUMERICAL, ) )
|
||||
ratings_services = services_manager.GetServices( ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) )
|
||||
|
||||
if len( ratings_like ) > 0: pred_classes.append( ClientGUIPredicates.PanelPredicateSystemRatingLike )
|
||||
if len( ratings_numerical ) > 0: pred_classes.append( ClientGUIPredicates.PanelPredicateSystemRatingNumerical )
|
||||
if len( ratings_services ) > 0:
|
||||
|
||||
pred_classes.append( ClientGUIPredicates.PanelPredicateSystemRating )
|
||||
|
||||
|
||||
elif predicate_type == HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO: pred_classes.append( ClientGUIPredicates.PanelPredicateSystemSimilarTo )
|
||||
elif predicate_type == HC.PREDICATE_TYPE_SYSTEM_SIZE: pred_classes.append( ClientGUIPredicates.PanelPredicateSystemSize )
|
||||
|
@ -3176,7 +3177,7 @@ class DialogPageChooser( Dialog ):
|
|||
|
||||
file_repos = [ ( 'page_query', service_key ) for service_key in [ service.GetServiceKey() for service in self._services if service.GetServiceType() == HC.FILE_REPOSITORY ] ]
|
||||
|
||||
entries = [ ( 'page_query', CC.LOCAL_FILE_SERVICE_KEY ) ] + file_repos
|
||||
entries = [ ( 'page_query', CC.LOCAL_FILE_SERVICE_KEY ), ( 'page_query', CC.TRASH_SERVICE_KEY ) ] + file_repos
|
||||
|
||||
elif menu_keyword == 'download': entries = [ ( 'page_import_url', None ), ( 'page_import_thread_watcher', None ), ( 'menu', 'gallery' ) ]
|
||||
elif menu_keyword == 'gallery':
|
||||
|
@ -3240,7 +3241,7 @@ class DialogPageChooser( Dialog ):
|
|||
|
||||
booru = dlg.GetBooru()
|
||||
|
||||
HydrusGlobals.pubsub.pub( 'new_import_gallery', HC.SITE_TYPE_BOORU, booru )
|
||||
HydrusGlobals.pubsub.pub( 'new_import_gallery', HC.SITE_TYPE_BOORU, booru.GetName() )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -7729,13 +7729,32 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def EventDelete( self, event ):
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'Delete this file from the database?' ) as dlg:
|
||||
locations_manager = self._current_media.GetLocationsManager()
|
||||
|
||||
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
text = 'Send this file to the trash?'
|
||||
|
||||
service_key = CC.LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
elif CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
text = 'Permanently delete this file?'
|
||||
|
||||
service_key = CC.TRASH_SERVICE_KEY
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
self._CommitCurrentChanges()
|
||||
|
||||
wx.GetApp().Write( 'content_updates', { CC.LOCAL_FILE_SERVICE_KEY : [ HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, ( self._current_media.GetHash(), ) ) ] } )
|
||||
wx.GetApp().Write( 'content_updates', { service_key : [ HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, ( self._current_media.GetHash(), ) ) ] } )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -411,11 +411,13 @@ class FullscreenHoverFrameRatings( FullscreenHoverFrame ):
|
|||
|
||||
self._icon_panel.SetBackgroundColour( wx.WHITE )
|
||||
|
||||
self._trash_icon = ClientGUICommon.BufferedWindowIcon( self._icon_panel, CC.GlobalBMPs.trash )
|
||||
self._inbox_icon = ClientGUICommon.BufferedWindowIcon( self._icon_panel, CC.GlobalBMPs.inbox )
|
||||
|
||||
icon_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
icon_hbox.AddF( ( 16, 16 ), CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
icon_hbox.AddF( self._trash_icon, CC.FLAGS_MIXED )
|
||||
icon_hbox.AddF( self._inbox_icon, CC.FLAGS_MIXED )
|
||||
|
||||
self._icon_panel.SetSizer( icon_hbox )
|
||||
|
@ -492,10 +494,31 @@ class FullscreenHoverFrameRatings( FullscreenHoverFrame ):
|
|||
|
||||
if self._current_media is not None:
|
||||
|
||||
if self._current_media.HasInbox():
|
||||
has_inbox = self._current_media.HasInbox()
|
||||
has_trash = CC.TRASH_SERVICE_KEY in self._current_media.GetLocationsManager().GetCurrent()
|
||||
|
||||
if has_inbox or has_trash:
|
||||
|
||||
self._icon_panel.Show()
|
||||
|
||||
if has_inbox:
|
||||
|
||||
self._inbox_icon.Show()
|
||||
|
||||
else:
|
||||
|
||||
self._inbox_icon.Hide()
|
||||
|
||||
|
||||
if has_trash:
|
||||
|
||||
self._trash_icon.Show()
|
||||
|
||||
else:
|
||||
|
||||
self._trash_icon.Hide()
|
||||
|
||||
|
||||
else:
|
||||
|
||||
self._icon_panel.Hide()
|
||||
|
|
|
@ -1935,10 +1935,10 @@ class ManagementPanelImportsGallery( ManagementPanelImports ):
|
|||
|
||||
def gallery_parsers_factory( raw_tags ):
|
||||
|
||||
booru = self._gallery_type
|
||||
booru_name = self._gallery_type
|
||||
tags = raw_tags.split( ' ' )
|
||||
|
||||
return ( ClientDownloading.GalleryParserBooru( booru, tags ), )
|
||||
return ( ClientDownloading.GalleryParserBooru( booru_name, tags ), )
|
||||
|
||||
|
||||
elif self._site_type == HC.SITE_TYPE_DEVIANT_ART:
|
||||
|
@ -2027,9 +2027,9 @@ class ManagementPanelImportsGallery( ManagementPanelImports ):
|
|||
|
||||
if self._site_type == HC.SITE_TYPE_BOORU:
|
||||
|
||||
booru = self._gallery_type
|
||||
name = self._gallery_type
|
||||
|
||||
name = booru.GetName()
|
||||
booru = wx.GetApp().Read( 'remote_booru', name )
|
||||
|
||||
namespaces = booru.GetNamespaces()
|
||||
initial_search_value = 'search tags'
|
||||
|
|
|
@ -94,7 +94,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
def _Archive( self ):
|
||||
|
||||
hashes = self._GetSelectedHashes( CC.DISCRIMINANT_INBOX )
|
||||
hashes = self._GetSelectedHashes( discriminant = CC.DISCRIMINANT_INBOX )
|
||||
|
||||
if len( hashes ) > 0:
|
||||
|
||||
|
@ -161,8 +161,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
if len( media_results ) > 0:
|
||||
|
||||
try: ClientGUICanvas.CanvasFullscreenMediaListCustomFilter( self.GetTopLevelParent(), self._page_key, self._file_service_key, media_results, shortcuts )
|
||||
except: wx.MessageBox( traceback.format_exc() )
|
||||
ClientGUICanvas.CanvasFullscreenMediaListCustomFilter( self.GetTopLevelParent(), self._page_key, self._file_service_key, media_results, shortcuts )
|
||||
|
||||
|
||||
|
||||
|
@ -170,39 +169,52 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
def _Delete( self, file_service_key ):
|
||||
|
||||
if file_service_key == CC.LOCAL_FILE_SERVICE_KEY:
|
||||
hashes = self._GetSelectedHashes( has_location = file_service_key )
|
||||
|
||||
num_to_delete = len( hashes )
|
||||
|
||||
if num_to_delete > 0:
|
||||
|
||||
hashes = self._GetSelectedHashes( CC.DISCRIMINANT_LOCAL )
|
||||
|
||||
num_to_delete = len( hashes )
|
||||
|
||||
if num_to_delete:
|
||||
if file_service_key == CC.LOCAL_FILE_SERVICE_KEY:
|
||||
|
||||
if num_to_delete == 1: message = 'Are you sure you want to delete this file?'
|
||||
else: message = 'Are you sure you want to delete these ' + HydrusData.ConvertIntToPrettyString( num_to_delete ) + ' files?'
|
||||
if num_to_delete == 1: text = 'Are you sure you want to send this file to the trash?'
|
||||
else: text = 'Are you sure you want send these ' + HydrusData.ConvertIntToPrettyString( num_to_delete ) + ' files to the trash?'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
||||
elif file_service_key == CC.TRASH_SERVICE_KEY:
|
||||
|
||||
if num_to_delete == 1: text = 'Are you sure you want to permanently delete this file?'
|
||||
else: text = 'Are you sure you want to permanently delete these ' + HydrusData.ConvertIntToPrettyString( num_to_delete ) + ' files?'
|
||||
|
||||
else:
|
||||
|
||||
if num_to_delete == 1: text = 'Are you sure you want to admin-delete this file?'
|
||||
else: text = 'Are you sure you want to admin-delete these ' + HydrusData.ConvertIntToPrettyString( num_to_delete ) + ' files?'
|
||||
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
if file_service_key in ( CC.LOCAL_FILE_SERVICE_KEY, CC.TRASH_SERVICE_KEY ):
|
||||
|
||||
self.SetFocussedMedia( self._page_key, None )
|
||||
if file_service_key == CC.TRASH_SERVICE_KEY:
|
||||
|
||||
self.SetFocussedMedia( self._page_key, None )
|
||||
|
||||
|
||||
try: wx.GetApp().Write( 'content_updates', { file_service_key : [ HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, hashes ) ] } )
|
||||
except: wx.MessageBox( traceback.format_exc() )
|
||||
wx.GetApp().Write( 'content_updates', { file_service_key : [ HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, hashes ) ] } )
|
||||
|
||||
else:
|
||||
|
||||
content_update = HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_PETITION, ( hashes, 'admin' ) )
|
||||
|
||||
service_keys_to_content_updates = { file_service_key : ( content_update, ) }
|
||||
|
||||
wx.GetApp().Write( 'content_updates', service_keys_to_content_updates )
|
||||
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
hashes = self._GetSelectedHashes()
|
||||
|
||||
content_update = HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_PETITION, ( hashes, 'admin' ) )
|
||||
|
||||
service_keys_to_content_updates = { file_service_key : ( content_update, ) }
|
||||
|
||||
wx.GetApp().Write( 'content_updates', service_keys_to_content_updates )
|
||||
|
||||
|
||||
|
||||
def _DeselectSelect( self, media_to_deselect, media_to_select ):
|
||||
|
@ -245,12 +257,11 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
def _Filter( self ):
|
||||
|
||||
media_results = self.GenerateMediaResults( discriminant = CC.DISCRIMINANT_LOCAL, selected_media = set( self._selected_media ) )
|
||||
media_results = self.GenerateMediaResults( has_location = CC.LOCAL_FILE_SERVICE_KEY, selected_media = set( self._selected_media ) )
|
||||
|
||||
if len( media_results ) > 0:
|
||||
|
||||
try: ClientGUICanvas.CanvasFullscreenMediaListFilterInbox( self.GetTopLevelParent(), self._page_key, self._file_service_key, media_results )
|
||||
except: wx.MessageBox( traceback.format_exc() )
|
||||
ClientGUICanvas.CanvasFullscreenMediaListFilterInbox( self.GetTopLevelParent(), self._page_key, self._file_service_key, media_results )
|
||||
|
||||
|
||||
|
||||
|
@ -302,11 +313,11 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
|
||||
|
||||
def _GetSelectedHashes( self, discriminant = None, not_uploaded_to = None ):
|
||||
def _GetSelectedHashes( self, has_location = None, discriminant = None, not_uploaded_to = None ):
|
||||
|
||||
result = set()
|
||||
|
||||
for media in self._selected_media: result.update( media.GetHashes( discriminant, not_uploaded_to ) )
|
||||
for media in self._selected_media: result.update( media.GetHashes( has_location, discriminant, not_uploaded_to ) )
|
||||
|
||||
return result
|
||||
|
||||
|
@ -381,7 +392,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
def _Inbox( self ):
|
||||
|
||||
hashes = self._GetSelectedHashes( CC.DISCRIMINANT_ARCHIVE )
|
||||
hashes = self._GetSelectedHashes( discriminant = CC.DISCRIMINANT_ARCHIVE )
|
||||
|
||||
if len( hashes ) > 0:
|
||||
|
||||
|
@ -515,13 +526,9 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
if len( media_results ) > 0:
|
||||
|
||||
try:
|
||||
|
||||
service = wx.GetApp().GetServicesManager().GetService( service_key )
|
||||
|
||||
if service.GetServiceType() == HC.LOCAL_RATING_LIKE: ClientGUICanvas.RatingsFilterFrameLike( self.GetTopLevelParent(), self._page_key, service_key, media_results )
|
||||
|
||||
except: wx.MessageBox( traceback.format_exc() )
|
||||
service = wx.GetApp().GetServicesManager().GetService( service_key )
|
||||
|
||||
if service.GetServiceType() == HC.LOCAL_RATING_LIKE: ClientGUICanvas.RatingsFilterFrameLike( self.GetTopLevelParent(), self._page_key, service_key, media_results )
|
||||
|
||||
|
||||
|
||||
|
@ -641,6 +648,27 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
|
||||
|
||||
def _Undelete( self ):
|
||||
|
||||
hashes = self._GetSelectedHashes( has_location = CC.TRASH_SERVICE_KEY )
|
||||
|
||||
num_to_undelete = len( hashes )
|
||||
|
||||
if num_to_undelete > 0:
|
||||
|
||||
if num_to_undelete == 1: text = 'Are you sure you want to undelete this file?'
|
||||
else: text = 'Are you sure you want to undelete these ' + HydrusData.ConvertIntToPrettyString( num_to_undelete ) + ' files?'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
wx.GetApp().Write( 'content_updates', { CC.TRASH_SERVICE_KEY : [ HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_UNDELETE, hashes ) ] } )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _UploadFiles( self, file_service_key ):
|
||||
|
||||
hashes = self._GetSelectedHashes( not_uploaded_to = file_service_key )
|
||||
|
@ -976,24 +1004,20 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
if len( self._selected_media ) > 0:
|
||||
|
||||
try:
|
||||
flat_media = []
|
||||
|
||||
for media in self._sorted_media:
|
||||
|
||||
flat_media = []
|
||||
|
||||
for media in self._sorted_media:
|
||||
if media in self._selected_media:
|
||||
|
||||
if media in self._selected_media:
|
||||
|
||||
if media.IsCollection(): flat_media.extend( media.GetFlatMedia() )
|
||||
else: flat_media.append( media )
|
||||
|
||||
if media.IsCollection(): flat_media.extend( media.GetFlatMedia() )
|
||||
else: flat_media.append( media )
|
||||
|
||||
|
||||
with ClientGUIDialogs.DialogSetupExport( None, flat_media ) as dlg: dlg.ShowModal()
|
||||
|
||||
self.SetFocus()
|
||||
|
||||
except: wx.MessageBox( traceback.format_exc() )
|
||||
|
||||
with ClientGUIDialogs.DialogSetupExport( None, flat_media ) as dlg: dlg.ShowModal()
|
||||
|
||||
self.SetFocus()
|
||||
|
||||
|
||||
|
||||
|
@ -1360,7 +1384,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
if command == 'archive': self._Archive()
|
||||
elif command == 'copy_bmp': self._CopyBMPToClipboard()
|
||||
elif command == 'copy_files':
|
||||
with wx.BusyCursor(): wx.GetApp().Write( 'copy_files', self._GetSelectedHashes( CC.DISCRIMINANT_LOCAL ) )
|
||||
with wx.BusyCursor(): wx.GetApp().Write( 'copy_files', self._GetSelectedHashes( discriminant = CC.DISCRIMINANT_LOCAL ) )
|
||||
elif command == 'copy_hash': self._CopyHashToClipboard()
|
||||
elif command == 'copy_hashes': self._CopyHashesToClipboard()
|
||||
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
|
||||
|
@ -1371,7 +1395,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
elif command == 'custom_filter': self._CustomFilter()
|
||||
elif command == 'delete': self._Delete( data )
|
||||
elif command == 'download': wx.GetApp().Write( 'content_updates', { CC.LOCAL_FILE_SERVICE_KEY : [ HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_PENDING, self._GetSelectedHashes( CC.DISCRIMINANT_NOT_LOCAL ) ) ] } )
|
||||
elif command == 'download': wx.GetApp().Write( 'content_updates', { CC.LOCAL_FILE_SERVICE_KEY : [ HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_FILES, HC.CONTENT_UPDATE_PENDING, self._GetSelectedHashes( discriminant = CC.DISCRIMINANT_NOT_LOCAL ) ) ] } )
|
||||
elif command == 'export_files': self._ExportFiles()
|
||||
elif command == 'export_tags': self._ExportTags()
|
||||
elif command == 'filter': self._Filter()
|
||||
|
@ -1394,6 +1418,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
elif command == 'select': self._Select( data )
|
||||
elif command == 'share_on_local_booru': self._ShareOnLocalBooru()
|
||||
elif command == 'show_selection_in_new_query_page': self._ShowSelectionInNewQueryPage()
|
||||
elif command == 'undelete': self._Undelete()
|
||||
elif command == 'upload': self._UploadFiles( data )
|
||||
elif command == 'key_up': self._MoveFocussedThumbnail( -1, 0, False )
|
||||
elif command == 'key_down': self._MoveFocussedThumbnail( 1, 0, False )
|
||||
|
@ -1510,7 +1535,8 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
all_locations_managers = [ media.GetLocationsManager() for media in self._selected_media ]
|
||||
|
||||
selection_has_local = True in ( locations_manager.HasLocal() for locations_manager in all_locations_managers )
|
||||
selection_has_local_file_service = True in ( CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent() for locations_manager in all_locations_managers )
|
||||
selection_has_trash = True in ( CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent() for locations_manager in all_locations_managers )
|
||||
selection_has_inbox = True in ( media.HasInbox() for media in self._selected_media )
|
||||
selection_has_archive = True in ( media.HasArchive() for media in self._selected_media )
|
||||
|
||||
|
@ -1596,6 +1622,8 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
inbox_phrase = 'return selected to inbox'
|
||||
remove_phrase = 'remove selected'
|
||||
local_delete_phrase = 'delete selected'
|
||||
trash_delete_phrase = 'delete selected from trash now'
|
||||
undelete_phrase = 'undelete selected'
|
||||
dump_phrase = 'dump selected to 4chan'
|
||||
export_phrase = 'files'
|
||||
copy_phrase = 'files'
|
||||
|
@ -1622,6 +1650,8 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
inbox_phrase = 'return to inbox'
|
||||
remove_phrase = 'remove'
|
||||
local_delete_phrase = 'delete'
|
||||
trash_delete_phrase = 'delete from trash now'
|
||||
undelete_phrase = 'undelete'
|
||||
dump_phrase = 'dump to 4chan'
|
||||
export_phrase = 'file'
|
||||
copy_phrase = 'file'
|
||||
|
@ -1675,7 +1705,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
# we can download (set pending to local) when we have permission, a file is not local and not already downloading and current
|
||||
|
||||
if not locations_manager.HasLocal() and not locations_manager.HasDownloading(): selection_downloadable_file_service_keys.update( downloadable_file_service_keys & locations_manager.GetCurrentRemote() )
|
||||
if not CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent() and not locations_manager.HasDownloading(): selection_downloadable_file_service_keys.update( downloadable_file_service_keys & locations_manager.GetCurrentRemote() )
|
||||
|
||||
# we can petition when we have permission and a file is current
|
||||
# we can re-petition an already petitioned file
|
||||
|
@ -1788,7 +1818,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
#
|
||||
|
||||
if selection_has_local and multiple_selected:
|
||||
if selection_has_local_file_service and multiple_selected:
|
||||
|
||||
filter_menu = wx.Menu()
|
||||
|
||||
|
@ -1801,20 +1831,27 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
menu.AppendSeparator()
|
||||
|
||||
if selection_has_local:
|
||||
if selection_has_inbox: menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'archive' ), archive_phrase )
|
||||
if selection_has_archive: menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'inbox' ), inbox_phrase )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'remove' ), remove_phrase )
|
||||
|
||||
if selection_has_local_file_service:
|
||||
|
||||
if selection_has_inbox: menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'archive' ), archive_phrase )
|
||||
if selection_has_archive: menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'inbox' ), inbox_phrase )
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'remove' ), remove_phrase )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.LOCAL_FILE_SERVICE_KEY ), local_delete_phrase )
|
||||
|
||||
|
||||
if selection_has_trash:
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', CC.TRASH_SERVICE_KEY ), trash_delete_phrase )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'undelete' ), undelete_phrase )
|
||||
|
||||
|
||||
# share
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally', CC.LOCAL_FILE_SERVICE_KEY ), '&open externally' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally' ), '&open externally' )
|
||||
|
||||
share_menu = wx.Menu()
|
||||
|
||||
|
@ -2325,8 +2362,34 @@ class Thumbnail( Selectable ):
|
|||
|
||||
locations_manager = self.GetLocationsManager()
|
||||
|
||||
if inbox: dc.DrawBitmap( CC.GlobalBMPs.inbox, width - 18, 0 )
|
||||
elif CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetPending(): dc.DrawBitmap( CC.GlobalBMPs.downloading, width - 18, 0 )
|
||||
icons_to_draw = []
|
||||
|
||||
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetPending():
|
||||
|
||||
icons_to_draw.append( CC.GlobalBMPs.downloading )
|
||||
|
||||
|
||||
if CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
icons_to_draw.append( CC.GlobalBMPs.trash )
|
||||
|
||||
|
||||
if inbox:
|
||||
|
||||
icons_to_draw.append( CC.GlobalBMPs.inbox )
|
||||
|
||||
|
||||
if len( icons_to_draw ) > 0:
|
||||
|
||||
icon_x = 0
|
||||
|
||||
for icon in icons_to_draw:
|
||||
|
||||
dc.DrawBitmap( icon, width + icon_x - 18, 0 )
|
||||
|
||||
icon_x -= 18
|
||||
|
||||
|
||||
|
||||
if self._dump_status == CC.DUMPER_DUMPED_OK: dc.DrawBitmap( CC.GlobalBMPs.dump_ok, width - 18, 18 )
|
||||
elif self._dump_status == CC.DUMPER_RECOVERABLE_ERROR: dc.DrawBitmap( CC.GlobalBMPs.dump_recoverable, width - 18, 18 )
|
||||
|
|
|
@ -503,7 +503,7 @@ class PanelPredicateSystemNumWords( PanelPredicateSystem ):
|
|||
return info
|
||||
|
||||
|
||||
class PanelPredicateSystemRatingLike( PanelPredicateSystem ):
|
||||
class PanelPredicateSystemRating( PanelPredicateSystem ):
|
||||
|
||||
PREDICATE_TYPE = HC.PREDICATE_TYPE_SYSTEM_RATING
|
||||
|
||||
|
@ -511,15 +511,17 @@ class PanelPredicateSystemRatingLike( PanelPredicateSystem ):
|
|||
|
||||
PanelPredicateSystem.__init__( self, parent )
|
||||
|
||||
#
|
||||
|
||||
local_like_services = wx.GetApp().GetServicesManager().GetServices( ( HC.LOCAL_RATING_LIKE, ) )
|
||||
|
||||
self._checkboxes_to_info = {}
|
||||
self._like_checkboxes_to_info = {}
|
||||
|
||||
self._rating_ctrls = []
|
||||
self._like_rating_ctrls = []
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 4 )
|
||||
gridbox_like = wx.FlexGridSizer( 0, 4 )
|
||||
|
||||
gridbox.AddGrowableCol( 0, 1 )
|
||||
gridbox_like.AddGrowableCol( 0, 1 )
|
||||
|
||||
for service in local_like_services:
|
||||
|
||||
|
@ -530,24 +532,68 @@ class PanelPredicateSystemRatingLike( PanelPredicateSystem ):
|
|||
not_rated_checkbox = wx.CheckBox( self, label = 'not rated' )
|
||||
rating_ctrl = ClientGUICommon.RatingLikeDialog( self, service_key )
|
||||
|
||||
self._checkboxes_to_info[ rated_checkbox ] = ( service_key, ClientRatings.SET )
|
||||
self._checkboxes_to_info[ not_rated_checkbox ] = ( service_key, ClientRatings.NULL )
|
||||
self._rating_ctrls.append( rating_ctrl )
|
||||
self._like_checkboxes_to_info[ rated_checkbox ] = ( service_key, ClientRatings.SET )
|
||||
self._like_checkboxes_to_info[ not_rated_checkbox ] = ( service_key, ClientRatings.NULL )
|
||||
self._like_rating_ctrls.append( rating_ctrl )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = name ), CC.FLAGS_MIXED )
|
||||
gridbox.AddF( rated_checkbox, CC.FLAGS_MIXED )
|
||||
gridbox.AddF( not_rated_checkbox, CC.FLAGS_MIXED )
|
||||
gridbox.AddF( rating_ctrl, CC.FLAGS_MIXED )
|
||||
gridbox_like.AddF( wx.StaticText( self, label = name ), CC.FLAGS_MIXED )
|
||||
gridbox_like.AddF( rated_checkbox, CC.FLAGS_MIXED )
|
||||
gridbox_like.AddF( not_rated_checkbox, CC.FLAGS_MIXED )
|
||||
gridbox_like.AddF( rating_ctrl, CC.FLAGS_MIXED )
|
||||
|
||||
|
||||
self.SetSizer( gridbox )
|
||||
#
|
||||
|
||||
local_numerical_services = wx.GetApp().GetServicesManager().GetServices( ( HC.LOCAL_RATING_NUMERICAL, ) )
|
||||
|
||||
self._numerical_checkboxes_to_info = {}
|
||||
|
||||
self._numerical_rating_ctrls_to_info = {}
|
||||
|
||||
gridbox_numerical = wx.FlexGridSizer( 0, 5 )
|
||||
|
||||
gridbox_numerical.AddGrowableCol( 0, 1 )
|
||||
|
||||
for service in local_numerical_services:
|
||||
|
||||
name = service.GetName()
|
||||
service_key = service.GetServiceKey()
|
||||
|
||||
rated_checkbox = wx.CheckBox( self, label = 'rated' )
|
||||
not_rated_checkbox = wx.CheckBox( self, label = 'not rated' )
|
||||
choice = wx.Choice( self, choices=[ '>', '<', '=', u'\u2248' ] )
|
||||
rating_ctrl = ClientGUICommon.RatingNumericalDialog( self, service_key )
|
||||
|
||||
choice.Select( 2 )
|
||||
|
||||
self._numerical_checkboxes_to_info[ rated_checkbox ] = ( service_key, ClientRatings.SET )
|
||||
self._numerical_checkboxes_to_info[ not_rated_checkbox ] = ( service_key, ClientRatings.NULL )
|
||||
self._numerical_rating_ctrls_to_info[ rating_ctrl ] = choice
|
||||
|
||||
gridbox_numerical.AddF( wx.StaticText( self, label = name ), CC.FLAGS_MIXED )
|
||||
gridbox_numerical.AddF( rated_checkbox, CC.FLAGS_MIXED )
|
||||
gridbox_numerical.AddF( not_rated_checkbox, CC.FLAGS_MIXED )
|
||||
gridbox_numerical.AddF( choice, CC.FLAGS_MIXED )
|
||||
gridbox_numerical.AddF( rating_ctrl, CC.FLAGS_MIXED )
|
||||
|
||||
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( gridbox_like, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
vbox.AddF( gridbox_numerical, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def GetInfo( self ):
|
||||
|
||||
infos = []
|
||||
|
||||
for ( checkbox, ( service_key, rating_state ) ) in self._checkboxes_to_info.items():
|
||||
#
|
||||
|
||||
for ( checkbox, ( service_key, rating_state ) ) in self._like_checkboxes_to_info.items():
|
||||
|
||||
if checkbox.GetValue() == True:
|
||||
|
||||
|
@ -564,7 +610,7 @@ class PanelPredicateSystemRatingLike( PanelPredicateSystem ):
|
|||
|
||||
|
||||
|
||||
for ctrl in self._rating_ctrls:
|
||||
for ctrl in self._like_rating_ctrls:
|
||||
|
||||
rating_state = ctrl.GetRatingState()
|
||||
|
||||
|
@ -585,67 +631,9 @@ class PanelPredicateSystemRatingLike( PanelPredicateSystem ):
|
|||
|
||||
|
||||
|
||||
return infos
|
||||
#
|
||||
|
||||
|
||||
def GetPredicates( self ):
|
||||
|
||||
infos = self.GetInfo()
|
||||
|
||||
predicates = [ HydrusData.Predicate( self.PREDICATE_TYPE, info ) for info in infos ]
|
||||
|
||||
return predicates
|
||||
|
||||
|
||||
class PanelPredicateSystemRatingNumerical( PanelPredicateSystem ):
|
||||
|
||||
PREDICATE_TYPE = HC.PREDICATE_TYPE_SYSTEM_RATING
|
||||
|
||||
def __init__( self, parent ):
|
||||
|
||||
PanelPredicateSystem.__init__( self, parent )
|
||||
|
||||
local_numerical_services = wx.GetApp().GetServicesManager().GetServices( ( HC.LOCAL_RATING_NUMERICAL, ) )
|
||||
|
||||
self._checkboxes_to_info = {}
|
||||
|
||||
self._rating_ctrls_to_info = {}
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 5 )
|
||||
|
||||
gridbox.AddGrowableCol( 0, 1 )
|
||||
|
||||
for service in local_numerical_services:
|
||||
|
||||
name = service.GetName()
|
||||
service_key = service.GetServiceKey()
|
||||
|
||||
rated_checkbox = wx.CheckBox( self, label = 'rated' )
|
||||
not_rated_checkbox = wx.CheckBox( self, label = 'not rated' )
|
||||
choice = wx.Choice( self, choices=[ '>', '<', '=', u'\u2248' ] )
|
||||
rating_ctrl = ClientGUICommon.RatingNumericalDialog( self, service_key )
|
||||
|
||||
choice.Select( 2 )
|
||||
|
||||
self._checkboxes_to_info[ rated_checkbox ] = ( service_key, ClientRatings.SET )
|
||||
self._checkboxes_to_info[ not_rated_checkbox ] = ( service_key, ClientRatings.NULL )
|
||||
self._rating_ctrls_to_info[ rating_ctrl ] = choice
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = name ), CC.FLAGS_MIXED )
|
||||
gridbox.AddF( rated_checkbox, CC.FLAGS_MIXED )
|
||||
gridbox.AddF( not_rated_checkbox, CC.FLAGS_MIXED )
|
||||
gridbox.AddF( choice, CC.FLAGS_MIXED )
|
||||
gridbox.AddF( rating_ctrl, CC.FLAGS_MIXED )
|
||||
|
||||
|
||||
self.SetSizer( gridbox )
|
||||
|
||||
|
||||
def GetInfo( self ):
|
||||
|
||||
infos = []
|
||||
|
||||
for ( checkbox, ( service_key, rating_state ) ) in self._checkboxes_to_info.items():
|
||||
for ( checkbox, ( service_key, rating_state ) ) in self._numerical_checkboxes_to_info.items():
|
||||
|
||||
if checkbox.GetValue() == True:
|
||||
|
||||
|
@ -662,7 +650,7 @@ class PanelPredicateSystemRatingNumerical( PanelPredicateSystem ):
|
|||
|
||||
|
||||
|
||||
for ( ctrl, choice ) in self._rating_ctrls_to_info.items():
|
||||
for ( ctrl, choice ) in self._numerical_rating_ctrls_to_info.items():
|
||||
|
||||
rating_state = ctrl.GetRatingState()
|
||||
|
||||
|
@ -678,6 +666,8 @@ class PanelPredicateSystemRatingNumerical( PanelPredicateSystem ):
|
|||
|
||||
|
||||
|
||||
#
|
||||
|
||||
return infos
|
||||
|
||||
|
||||
|
|
|
@ -179,12 +179,22 @@ class MediaList( object ):
|
|||
for media in self._collected_media: media.DeletePending( service_key )
|
||||
|
||||
|
||||
def GenerateMediaResults( self, discriminant = None, selected_media = None, unrated = None ):
|
||||
def GenerateMediaResults( self, has_location = None, discriminant = None, selected_media = None, unrated = None ):
|
||||
|
||||
media_results = []
|
||||
|
||||
for media in self._sorted_media:
|
||||
|
||||
if has_location is not None:
|
||||
|
||||
locations_manager = media.GetLocationsManager()
|
||||
|
||||
if has_location not in locations_manager.GetCurrent():
|
||||
|
||||
continue
|
||||
|
||||
|
||||
|
||||
if selected_media is not None and media not in selected_media: continue
|
||||
|
||||
if media.IsCollection(): media_results.extend( media.GenerateMediaResults( discriminant ) )
|
||||
|
@ -253,12 +263,19 @@ class MediaList( object ):
|
|||
|
||||
if data_type == HC.CONTENT_DATA_TYPE_FILES:
|
||||
|
||||
if action == HC.CONTENT_UPDATE_DELETE and service_key == self._file_service_key:
|
||||
if action == HC.CONTENT_UPDATE_DELETE:
|
||||
|
||||
affected_singleton_media = self._GetMedia( hashes, 'singletons' )
|
||||
affected_collected_media = [ media for media in self._collected_media if media.HasNoMedia() ]
|
||||
permanently_deleted = service_key == CC.TRASH_SERVICE_KEY and self._file_service_key in ( CC.TRASH_SERVICE_KEY, CC.LOCAL_FILE_SERVICE_KEY )
|
||||
|
||||
self._RemoveMedia( affected_singleton_media, affected_collected_media )
|
||||
repo_deleted = service_key not in ( CC.LOCAL_FILE_SERVICE_KEY, CC.TRASH_SERVICE_KEY ) and service_key == self._file_service_key
|
||||
|
||||
if permanently_deleted or repo_deleted:
|
||||
|
||||
affected_singleton_media = self._GetMedia( hashes, 'singletons' )
|
||||
affected_collected_media = [ media for media in self._collected_media if media.HasNoMedia() ]
|
||||
|
||||
self._RemoveMedia( affected_singleton_media, affected_collected_media )
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -542,14 +559,14 @@ class MediaCollection( MediaList, Media ):
|
|||
|
||||
def GetHash( self ): return self.GetDisplayMedia().GetHash()
|
||||
|
||||
def GetHashes( self, discriminant = None, not_uploaded_to = None ):
|
||||
def GetHashes( self, has_location = None, discriminant = None, not_uploaded_to = None ):
|
||||
|
||||
if discriminant is None and not_uploaded_to is None: return self._hashes
|
||||
if has_location is None and discriminant is None and not_uploaded_to is None: return self._hashes
|
||||
else:
|
||||
|
||||
result = set()
|
||||
|
||||
for media in self._sorted_media: result.update( media.GetHashes( discriminant, not_uploaded_to ) )
|
||||
for media in self._sorted_media: result.update( media.GetHashes( has_location, discriminant, not_uploaded_to ) )
|
||||
|
||||
return result
|
||||
|
||||
|
@ -646,20 +663,23 @@ class MediaSingleton( Media ):
|
|||
|
||||
def GetHash( self ): return self._media_result.GetHash()
|
||||
|
||||
def GetHashes( self, discriminant = None, not_uploaded_to = None ):
|
||||
def GetHashes( self, has_location = None, discriminant = None, not_uploaded_to = None ):
|
||||
|
||||
locations_manager = self._media_result.GetLocationsManager()
|
||||
|
||||
if discriminant is not None:
|
||||
|
||||
inbox = self._media_result.GetInbox()
|
||||
|
||||
locations_manager = self._media_result.GetLocationsManager()
|
||||
|
||||
if ( discriminant == CC.DISCRIMINANT_INBOX and not inbox ) or ( discriminant == CC.DISCRIMINANT_ARCHIVE and inbox ) or ( discriminant == CC.DISCRIMINANT_LOCAL and not locations_manager.HasLocal() ) or ( discriminant == CC.DISCRIMINANT_NOT_LOCAL and locations_manager.HasLocal() ): return set()
|
||||
|
||||
|
||||
if not_uploaded_to is not None:
|
||||
if has_location is not None:
|
||||
|
||||
locations_manager = self._media_result.GetLocationsManager()
|
||||
if has_location not in locations_manager.GetCurrent(): return set()
|
||||
|
||||
|
||||
if not_uploaded_to is not None:
|
||||
|
||||
if not_uploaded_to in locations_manager.GetCurrentRemote(): return set()
|
||||
|
||||
|
@ -681,6 +701,7 @@ class MediaSingleton( Media ):
|
|||
|
||||
if self.HasInbox(): return 1
|
||||
else: return 0
|
||||
|
||||
|
||||
def GetNumWords( self ): return self._media_result.GetNumWords()
|
||||
|
||||
|
@ -910,12 +931,10 @@ class MediaResult( object ):
|
|||
if service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ): tags_manager.ProcessContentUpdate( service_key, content_update )
|
||||
elif service_type in ( HC.FILE_REPOSITORY, HC.LOCAL_FILE ):
|
||||
|
||||
if service_type == HC.LOCAL_FILE:
|
||||
if service_key == CC.LOCAL_FILE_SERVICE_KEY:
|
||||
|
||||
if action == HC.CONTENT_UPDATE_ADD and not locations_manager.HasLocal(): inbox = True
|
||||
elif action == HC.CONTENT_UPDATE_ARCHIVE: inbox = False
|
||||
if action == HC.CONTENT_UPDATE_ARCHIVE: inbox = False
|
||||
elif action == HC.CONTENT_UPDATE_INBOX: inbox = True
|
||||
elif action == HC.CONTENT_UPDATE_DELETE: inbox = False
|
||||
|
||||
self._tuple = ( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings )
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 17
|
||||
SOFTWARE_VERSION = 163
|
||||
SOFTWARE_VERSION = 164
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
@ -80,6 +80,7 @@ CONTENT_UPDATE_RATING = 9
|
|||
CONTENT_UPDATE_DENY_PEND = 11
|
||||
CONTENT_UPDATE_DENY_PETITION = 12
|
||||
CONTENT_UPDATE_ADVANCED = 13
|
||||
CONTENT_UPDATE_UNDELETE = 14
|
||||
|
||||
content_update_string_lookup = {}
|
||||
|
||||
|
@ -95,6 +96,7 @@ content_update_string_lookup[ CONTENT_UPDATE_INBOX ] = 'inbox'
|
|||
content_update_string_lookup[ CONTENT_UPDATE_RATING ] = 'rating'
|
||||
content_update_string_lookup[ CONTENT_UPDATE_DENY_PEND ] = 'deny pend'
|
||||
content_update_string_lookup[ CONTENT_UPDATE_DENY_PETITION ] = 'deny petition'
|
||||
content_update_string_lookup[ CONTENT_UPDATE_UNDELETE ] = 'undelete'
|
||||
|
||||
IMPORT_FOLDER_TYPE_DELETE = 0
|
||||
IMPORT_FOLDER_TYPE_SYNCHRONISE = 1
|
||||
|
|
|
@ -214,7 +214,7 @@ def ConvertShortcutToPrettyShortcut( modifier, key ):
|
|||
|
||||
def ConvertSiteTypeGalleryTypeToPrettyString( site_type, gallery_type ):
|
||||
|
||||
if site_type == HC.SITE_TYPE_BOORU: s = gallery_type.GetName()
|
||||
if site_type == HC.SITE_TYPE_BOORU: s = gallery_type
|
||||
else:
|
||||
|
||||
s = HC.site_type_string_lookup[ site_type ]
|
||||
|
@ -1089,7 +1089,7 @@ class ContentUpdate( object ):
|
|||
|
||||
hashes = set( ( hash, ) )
|
||||
|
||||
elif self._action in ( HC.CONTENT_UPDATE_ARCHIVE, HC.CONTENT_UPDATE_DELETE, HC.CONTENT_UPDATE_INBOX, HC.CONTENT_UPDATE_PENDING, HC.CONTENT_UPDATE_RESCIND_PENDING, HC.CONTENT_UPDATE_RESCIND_PETITION ): hashes = self._row
|
||||
elif self._action in ( HC.CONTENT_UPDATE_ARCHIVE, HC.CONTENT_UPDATE_DELETE, HC.CONTENT_UPDATE_UNDELETE, HC.CONTENT_UPDATE_INBOX, HC.CONTENT_UPDATE_PENDING, HC.CONTENT_UPDATE_RESCIND_PENDING, HC.CONTENT_UPDATE_RESCIND_PETITION ): hashes = self._row
|
||||
elif self._action == HC.CONTENT_UPDATE_PETITION: ( hashes, reason ) = self._row
|
||||
|
||||
elif self._data_type == HC.CONTENT_DATA_TYPE_MAPPINGS:
|
||||
|
|
|
@ -177,7 +177,9 @@ class TestDownloaders( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
downloader = ClientDownloading.GalleryParserBooru( ClientDefaults.GetDefaultBoorus()[ 'sankaku chan' ], [ 'animal_ears' ] )
|
||||
wx.GetApp().SetRead( 'remote_booru', ClientDefaults.GetDefaultBoorus()[ 'sankaku chan' ] )
|
||||
|
||||
downloader = ClientDownloading.GalleryParserBooru( 'sankaku chan', [ 'animal_ears' ] )
|
||||
|
||||
#
|
||||
|
||||
|
@ -243,7 +245,9 @@ class TestDownloaders( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
downloader = ClientDownloading.GalleryParserBooru( ClientDefaults.GetDefaultBoorus()[ 'e621' ], [ 'hair' ] )
|
||||
wx.GetApp().SetRead( 'remote_booru', ClientDefaults.GetDefaultBoorus()[ 'e621' ] )
|
||||
|
||||
downloader = ClientDownloading.GalleryParserBooru( 'e621', [ 'hair' ] )
|
||||
|
||||
#
|
||||
|
||||
|
|
|
@ -997,7 +997,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
result_service_keys = { service.GetServiceKey() for service in result }
|
||||
|
||||
self.assertItemsEqual( { CC.LOCAL_FILE_SERVICE_KEY, CC.LOCAL_TAG_SERVICE_KEY }, result_service_keys )
|
||||
self.assertItemsEqual( { CC.TRASH_SERVICE_KEY, CC.LOCAL_FILE_SERVICE_KEY, CC.LOCAL_TAG_SERVICE_KEY }, result_service_keys )
|
||||
|
||||
#
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 363 B |
Loading…
Reference in New Issue