Version 247

This commit is contained in:
Hydrus Network Developer 2017-03-15 15:13:04 -05:00
parent 8fde94e898
commit 7d4639ed83
35 changed files with 1637 additions and 1148 deletions

View File

@ -8,6 +8,37 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 247</h3></li>
<ul>
<li>fixed a problem with deleting more than 256 files at once</li>
<li>furthermore, deleting from thumbnail view or after a filter will split delete jobs into chunks of 64 files at a time to reduce gui hang from deleting many tag-heavy files</li>
<li>rewrote canvas media container code to recycle containers, embed windows, animation bars, static image windows, and animation windows. scrolling through all kinds of media is less flickery (less 'grey box' window initialise flicker) and scrolling through static images should be completely flickerless! William Gibson slideshow speed works again!</li>
<li>this flickerless static image transition will be particularly useful in the forthcoming duplicate image filter!</li>
<li>got adminside petition processing working again</li>
<li>petition counts and fetching is now split by content_type and status</li>
<li>the approve/deny colour hint is more obvious on the petition panel</li>
<li>petitions now process off the main gui thread and throw up a popup message</li>
<li>added 'check all' and 'check none' buttons to petition panel</li>
<li>several serverside petition processing fixes</li>
<li>generalised and improved dynamic menu check item initialisation and inversion support</li>
<li>moved the 'get tags even if file already in db' option into a cog button on regular downloader pages</li>
<li>added a default menu option for 'get tags even if file already in db' to the same cog button</li>
<li>added this cog button to the edit subscription panel as well</li>
<li>fixed exporting tags to Hydrus Tag Archives</li>
<li>fixed exporting 'all known tags' to HTAs</li>
<li>cleaned some HTA and related code</li>
<li>fixed namespace based tag censorship</li>
<li>fixed autocomplete not filtering out current/pending counts if they are set to 'excluded'</li>
<li>fixed generation of non-expiring new accounts</li>
<li>fixed some v245->v246 tag improvement code that was replacing invalid tags with the incorrect namespace</li>
<li>patched a problem with '-:' dirty tag in the v245->v246 update code--I'm not sure what it was doing, but it catches the unusual problem and puts it in the 'invalid tags' category, so let me know if you get trouble with this in future</li>
<li>wrote unit tests for bytesdictionary and shortcuts serialisable objects</li>
<li>harmonised and improved how separators are appended to menus</li>
<li>cleaned up some client db index creation</li>
<li>cleaned up some client tuple stripping</li>
<li>misc pylint warnings cleanup</li>
<li>misc fixes</li>
</ul>
<li><h3>version 246</h3></li>
<ul>
<li>fixed a critical bug in serverside content deserialisating that meant servers were not processing most client-submitted data properly</li>

View File

@ -1,6 +1,5 @@
import ClientDefaults
import ClientDownloading
import ClientFiles
import ClientNetworking
import ClientRendering
import ClientSearch
@ -8,16 +7,13 @@ import ClientThreading
import HydrusConstants as HC
import HydrusExceptions
import HydrusFileHandling
import HydrusImageHandling
import HydrusPaths
import HydrusSessions
import itertools
import json
import os
import random
import Queue
import requests
import shutil
import threading
import time
import urllib

View File

@ -1,10 +1,5 @@
import collections
import HydrusConstants as HC
import HydrusExceptions
import os
import sys
import threading
import traceback
import wx
import wx.lib.newevent

View File

@ -6,7 +6,6 @@ import ClientGUIMenus
import ClientNetworking
import ClientThreading
import hashlib
import httplib
import HydrusConstants as HC
import HydrusController
import HydrusData
@ -14,8 +13,6 @@ import HydrusExceptions
import HydrusGlobals
import HydrusNetworking
import HydrusSerialisable
import HydrusSessions
import HydrusTags
import HydrusThreading
import HydrusVideoHandling
import ClientConstants as CC
@ -24,10 +21,6 @@ import ClientGUI
import ClientGUIDialogs
import os
import psutil
import random
import sqlite3
import subprocess
import sys
import threading
import time
import traceback
@ -687,7 +680,7 @@ class Controller( HydrusController.HydrusController ):
disk_cache_stop_time = HydrusData.GetNow() + 6
loaded_into_disk_cache = HydrusGlobals.client_controller.Read( 'load_into_disk_cache', stop_time = disk_cache_stop_time, caller_limit = disk_cache_maintenance_mb * 1024 * 1024 )
HydrusGlobals.client_controller.Read( 'load_into_disk_cache', stop_time = disk_cache_stop_time, caller_limit = disk_cache_maintenance_mb * 1024 * 1024 )
if self._new_options.GetBoolean( 'maintain_similar_files_duplicate_pairs_during_idle' ):

View File

@ -1,50 +1,37 @@
import ClientData
import ClientDefaults
import ClientDownloading
import ClientFiles
import ClientImageHandling
import ClientImporting
import ClientMedia
import ClientRatings
import ClientSearch
import ClientServices
import ClientThreading
import collections
import gc
import hashlib
import httplib
import itertools
import json
import HydrusConstants as HC
import HydrusData
import HydrusDB
import HydrusExceptions
import HydrusFileHandling
import HydrusGlobals
import HydrusImageHandling
import HydrusNATPunch
import HydrusNetwork
import HydrusPaths
import HydrusSerialisable
import HydrusTagArchive
import HydrusTags
import HydrusThreading
import ClientConstants as CC
import numpy
import os
import psutil
import Queue
import random
import re
import shutil
import sqlite3
import stat
import sys
import threading
import time
import traceback
import wx
import yaml
import HydrusData
import ClientSearch
import HydrusGlobals
YAML_DUMP_ID_SINGLE = 0
YAML_DUMP_ID_REMOTE_BOORU = 1
@ -201,7 +188,7 @@ class DB( HydrusDB.HydrusDB ):
select_statement = 'SELECT COUNT( * ) FROM files_info WHERE mime IN ' + HydrusData.SplayListForDB( HC.SEARCHABLE_MIMES ) + ' AND hash_id IN %s;'
[ ( num_viewable_files, ) ] = self._SelectFromList( select_statement, valid_hash_ids )
num_viewable_files = sum( self._STL( self._SelectFromList( select_statement, valid_hash_ids ) ) )
service_info_updates.append( ( num_viewable_files, service_id, HC.SERVICE_INFO_NUM_VIEWABLE_FILES ) )
@ -290,22 +277,17 @@ class DB( HydrusDB.HydrusDB ):
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( service_id )
current_mappings_table_simple_name = current_mappings_table_name.split( '.' )[1]
deleted_mappings_table_simple_name = deleted_mappings_table_name.split( '.' )[1]
pending_mappings_table_simple_name = pending_mappings_table_name.split( '.' )[1]
petitioned_mappings_table_simple_name = petitioned_mappings_table_name.split( '.' )[1]
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + current_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._c.execute( 'CREATE UNIQUE INDEX IF NOT EXISTS ' + current_mappings_table_name + '_hash_id_tag_id_index ON ' + current_mappings_table_simple_name + ' ( hash_id, tag_id );' )
self._CreateIndex( current_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + deleted_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._c.execute( 'CREATE UNIQUE INDEX IF NOT EXISTS ' + deleted_mappings_table_name + '_hash_id_tag_id_index ON ' + deleted_mappings_table_simple_name + ' ( hash_id, tag_id );' )
self._CreateIndex( deleted_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + pending_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._c.execute( 'CREATE UNIQUE INDEX IF NOT EXISTS ' + pending_mappings_table_name + '_hash_id_tag_id_index ON ' + pending_mappings_table_simple_name + ' ( hash_id, tag_id );' )
self._CreateIndex( pending_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
self._c.execute( 'CREATE TABLE IF NOT EXISTS ' + petitioned_mappings_table_name + ' ( tag_id INTEGER, hash_id INTEGER, reason_id INTEGER, PRIMARY KEY ( tag_id, hash_id ) ) WITHOUT ROWID;' )
self._c.execute( 'CREATE UNIQUE INDEX IF NOT EXISTS ' + petitioned_mappings_table_name + '_hash_id_tag_id_index ON ' + petitioned_mappings_table_simple_name + ' ( hash_id, tag_id );' )
self._CreateIndex( petitioned_mappings_table_name, [ 'hash_id', 'tag_id' ], unique = True )
#
@ -2091,25 +2073,25 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'CREATE TABLE client_files_locations ( prefix TEXT, location TEXT );' )
self._c.execute( 'CREATE TABLE current_files ( service_id INTEGER REFERENCES services ON DELETE CASCADE, hash_id INTEGER, timestamp INTEGER, PRIMARY KEY ( service_id, hash_id ) );' )
self._c.execute( 'CREATE INDEX current_files_timestamp ON current_files ( timestamp );' )
self._CreateIndex( 'current_files', [ 'timestamp' ] )
self._c.execute( 'CREATE TABLE deleted_files ( service_id INTEGER REFERENCES services ON DELETE CASCADE, hash_id INTEGER, PRIMARY KEY ( service_id, hash_id ) );' )
self._c.execute( 'CREATE TABLE file_inbox ( hash_id INTEGER PRIMARY KEY );' )
self._c.execute( 'CREATE TABLE files_info ( hash_id INTEGER PRIMARY KEY, size INTEGER, mime INTEGER, width INTEGER, height INTEGER, duration INTEGER, num_frames INTEGER, num_words INTEGER );' )
self._c.execute( 'CREATE INDEX files_info_size ON files_info ( size );' )
self._c.execute( 'CREATE INDEX files_info_mime ON files_info ( mime );' )
self._c.execute( 'CREATE INDEX files_info_width ON files_info ( width );' )
self._c.execute( 'CREATE INDEX files_info_height ON files_info ( height );' )
self._c.execute( 'CREATE INDEX files_info_duration ON files_info ( duration );' )
self._c.execute( 'CREATE INDEX files_info_num_frames ON files_info ( num_frames );' )
self._CreateIndex( 'files_info', [ 'size' ] )
self._CreateIndex( 'files_info', [ 'mime' ] )
self._CreateIndex( 'files_info', [ 'width' ] )
self._CreateIndex( 'files_info', [ 'height' ] )
self._CreateIndex( 'files_info', [ 'duration' ] )
self._CreateIndex( 'files_info', [ 'num_frames' ] )
self._c.execute( 'CREATE TABLE file_transfers ( service_id INTEGER REFERENCES services ON DELETE CASCADE, hash_id INTEGER, PRIMARY KEY ( service_id, hash_id ) );' )
self._c.execute( 'CREATE INDEX file_transfers_hash_id ON file_transfers ( hash_id );' )
self._CreateIndex( 'file_transfers', [ 'hash_id' ] )
self._c.execute( 'CREATE TABLE file_petitions ( service_id INTEGER REFERENCES services ON DELETE CASCADE, hash_id INTEGER, reason_id INTEGER, PRIMARY KEY ( service_id, hash_id, reason_id ) );' )
self._c.execute( 'CREATE INDEX file_petitions_hash_id_index ON file_petitions ( hash_id );' )
self._CreateIndex( 'file_petitions', [ 'hash_id' ] )
self._c.execute( 'CREATE TABLE hydrus_sessions ( service_id INTEGER PRIMARY KEY REFERENCES services ON DELETE CASCADE, session_key BLOB_BYTES, expiry INTEGER );' )
@ -2118,22 +2100,22 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'CREATE TABLE json_dumps_named ( dump_type INTEGER, dump_name TEXT, version INTEGER, dump BLOB_BYTES, PRIMARY KEY ( dump_type, dump_name ) );' )
self._c.execute( 'CREATE TABLE local_hashes ( hash_id INTEGER PRIMARY KEY, md5 BLOB_BYTES, sha1 BLOB_BYTES, sha512 BLOB_BYTES );' )
self._c.execute( 'CREATE INDEX local_hashes_md5_index ON local_hashes ( md5 );' )
self._c.execute( 'CREATE INDEX local_hashes_sha1_index ON local_hashes ( sha1 );' )
self._c.execute( 'CREATE INDEX local_hashes_sha512_index ON local_hashes ( sha512 );' )
self._CreateIndex( 'local_hashes', [ 'md5' ] )
self._CreateIndex( 'local_hashes', [ 'sha1' ] )
self._CreateIndex( 'local_hashes', [ 'sha512' ] )
self._c.execute( 'CREATE TABLE local_ratings ( service_id INTEGER REFERENCES services ON DELETE CASCADE, hash_id INTEGER, rating REAL, PRIMARY KEY ( service_id, hash_id ) );' )
self._c.execute( 'CREATE INDEX local_ratings_hash_id_index ON local_ratings ( hash_id );' )
self._c.execute( 'CREATE INDEX local_ratings_rating_index ON local_ratings ( rating );' )
self._CreateIndex( 'local_ratings', [ 'hash_id' ] )
self._CreateIndex( 'local_ratings', [ 'rating' ] )
self._c.execute( 'CREATE TABLE options ( options TEXT_YAML );', )
self._c.execute( 'CREATE TABLE recent_tags ( service_id INTEGER REFERENCES services ON DELETE CASCADE, tag_id INTEGER, timestamp INTEGER, PRIMARY KEY ( service_id, tag_id ) );' )
self._c.execute( 'CREATE TABLE remote_ratings ( service_id INTEGER REFERENCES services ON DELETE CASCADE, hash_id INTEGER, count INTEGER, rating REAL, score REAL, PRIMARY KEY ( service_id, hash_id ) );' )
self._c.execute( 'CREATE INDEX remote_ratings_hash_id_index ON remote_ratings ( hash_id );' )
self._c.execute( 'CREATE INDEX remote_ratings_rating_index ON remote_ratings ( rating );' )
self._c.execute( 'CREATE INDEX remote_ratings_score_index ON remote_ratings ( score );' )
self._CreateIndex( 'remote_ratings', [ 'hash_id' ] )
self._CreateIndex( 'remote_ratings', [ 'rating' ] )
self._CreateIndex( 'remote_ratings', [ 'score' ] )
self._c.execute( 'CREATE TABLE remote_thumbnails ( service_id INTEGER, hash_id INTEGER, PRIMARY KEY( service_id, hash_id ) );' )
@ -2143,8 +2125,7 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'CREATE TABLE service_info ( service_id INTEGER REFERENCES services ON DELETE CASCADE, info_type INTEGER, info INTEGER, PRIMARY KEY ( service_id, info_type ) );' )
self._c.execute( 'CREATE TABLE statuses ( status_id INTEGER PRIMARY KEY, status TEXT );' )
self._c.execute( 'CREATE UNIQUE INDEX statuses_status_index ON statuses ( status );' )
self._c.execute( 'CREATE TABLE statuses ( status_id INTEGER PRIMARY KEY, status TEXT UNIQUE );' )
self._c.execute( 'CREATE TABLE tag_censorship ( service_id INTEGER PRIMARY KEY REFERENCES services ON DELETE CASCADE, blacklist INTEGER_BOOLEAN, tags TEXT_YAML );' )
@ -2157,7 +2138,7 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'CREATE TABLE tag_sibling_petitions ( service_id INTEGER REFERENCES services ON DELETE CASCADE, bad_tag_id INTEGER, good_tag_id INTEGER, status INTEGER, reason_id INTEGER, PRIMARY KEY ( service_id, bad_tag_id, status ) );' )
self._c.execute( 'CREATE TABLE urls ( url TEXT PRIMARY KEY, hash_id INTEGER );' )
self._c.execute( 'CREATE INDEX urls_hash_id ON urls ( hash_id );' )
self._CreateIndex( 'urls', [ 'hash_id' ] )
self._c.execute( 'CREATE TABLE vacuum_timestamps ( name TEXT, timestamp INTEGER );' )
@ -2172,10 +2153,10 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'CREATE TABLE external_caches.shape_perceptual_hashes ( phash_id INTEGER PRIMARY KEY, phash BLOB_BYTES UNIQUE );' )
self._c.execute( 'CREATE TABLE external_caches.shape_perceptual_hash_map ( phash_id INTEGER, hash_id INTEGER, PRIMARY KEY ( phash_id, hash_id ) );' )
self._c.execute( 'CREATE INDEX external_caches.shape_perceptual_hash_map_hash_id_index ON shape_perceptual_hash_map ( hash_id );' )
self._CreateIndex( 'external_caches.shape_perceptual_hash_map', [ 'hash_id' ] )
self._c.execute( 'CREATE TABLE external_caches.shape_vptree ( phash_id INTEGER PRIMARY KEY, parent_id INTEGER, radius INTEGER, inner_id INTEGER, inner_population INTEGER, outer_id INTEGER, outer_population INTEGER );' )
self._c.execute( 'CREATE INDEX external_caches.shape_vptree_parent_id_index ON shape_vptree ( parent_id );' )
self._CreateIndex( 'external_caches.shape_vptree', [ 'parent_id' ] )
self._c.execute( 'CREATE TABLE external_caches.shape_maintenance_phash_regen ( hash_id INTEGER PRIMARY KEY );' )
self._c.execute( 'CREATE TABLE external_caches.shape_maintenance_branch_regen ( phash_id INTEGER PRIMARY KEY );' )
@ -2183,7 +2164,7 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'CREATE TABLE external_caches.shape_search_cache ( hash_id INTEGER PRIMARY KEY, searched_distance INTEGER );' )
self._c.execute( 'CREATE TABLE external_caches.duplicate_pairs ( smaller_hash_id INTEGER, larger_hash_id INTEGER, duplicate_type INTEGER, PRIMARY KEY ( smaller_hash_id, larger_hash_id ) );' )
self._c.execute( 'CREATE UNIQUE INDEX external_caches.duplicate_pairs_reversed_hash_ids ON duplicate_pairs ( larger_hash_id, smaller_hash_id );' )
self._CreateIndex( 'external_caches.duplicate_pairs', [ 'larger_hash_id', 'smaller_hash_id' ], unique = True )
# master
@ -2196,7 +2177,7 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'CREATE VIRTUAL TABLE IF NOT EXISTS external_master.subtags_fts4 USING fts4( subtag );' )
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.tags ( tag_id INTEGER PRIMARY KEY, namespace_id INTEGER, subtag_id INTEGER );' )
self._c.execute( 'CREATE UNIQUE INDEX external_master.tags_subtag_id_namespace_id_index ON tags ( subtag_id, namespace_id );' )
self._CreateIndex( 'external_master.tags', [ 'subtag_id', 'namespace_id' ] )
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.texts ( text_id INTEGER PRIMARY KEY, text TEXT UNIQUE );' )
@ -2278,7 +2259,7 @@ class DB( HydrusDB.HydrusDB ):
select_statement = 'SELECT COUNT( * ) FROM files_info WHERE mime IN ' + HydrusData.SplayListForDB( HC.SEARCHABLE_MIMES ) + ' AND hash_id IN %s;'
[ ( num_viewable_files, ) ] = self._SelectFromList( select_statement, valid_hash_ids )
num_viewable_files = sum( self._STL( self._SelectFromList( select_statement, valid_hash_ids ) ) )
service_info_updates.append( ( -num_viewable_files, service_id, HC.SERVICE_INFO_NUM_VIEWABLE_FILES ) )
@ -2573,13 +2554,11 @@ class DB( HydrusDB.HydrusDB ):
service_id = self._GetServiceId( service_key )
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( service_id )
hta_exists = os.path.exists( path )
hta = HydrusTagArchive.HydrusTagArchive( path )
if hta_exists and hta.GetHashType() != hash_type:
if hta_exists and hta.HasHashTypeSet() and hta.GetHashType() != hash_type:
raise Exception( 'This tag archive does not use the expected hash type, so it cannot be exported to!' )
@ -2596,42 +2575,64 @@ class DB( HydrusDB.HydrusDB ):
hash_ids = self._GetHashIds( hashes )
service_ids_to_export_from = []
if service_key == CC.COMBINED_TAG_SERVICE_KEY:
service_ids_to_export_from = self._GetServiceIds( ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ) )
else:
service_ids_to_export_from = [ service_id ]
hta.BeginBigJob()
for ( i, hash_id ) in enumerate( hash_ids ):
for service_id in service_ids_to_export_from:
( i_paused, should_quit ) = job_key.WaitIfNeeded()
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( service_id )
if should_quit:
for ( i, hash_id ) in enumerate( hash_ids ):
return
( i_paused, should_quit ) = job_key.WaitIfNeeded()
if i % 100 == 0:
if should_quit:
return
job_key.SetVariable( 'popup_text_1', prefix_string + HydrusData.ConvertValueRangeToPrettyString( i, len( hash_ids ) ) )
job_key.SetVariable( 'popup_gauge_1', ( i, len( hash_ids ) ) )
if i % 100 == 0:
job_key.SetVariable( 'popup_text_1', prefix_string + HydrusData.ConvertValueRangeToPrettyString( i, len( hash_ids ) ) )
job_key.SetVariable( 'popup_gauge_1', ( i, len( hash_ids ) ) )
if hash_type == HydrusTagArchive.HASH_TYPE_SHA256: archive_hash = self._GetHash( hash_id )
else:
if hash_type == HydrusTagArchive.HASH_TYPE_SHA256:
archive_hash = self._GetHash( hash_id )
else:
if hash_type == HydrusTagArchive.HASH_TYPE_MD5: h = 'md5'
elif hash_type == HydrusTagArchive.HASH_TYPE_SHA1: h = 'sha1'
elif hash_type == HydrusTagArchive.HASH_TYPE_SHA512: h = 'sha512'
result = self._c.execute( 'SELECT ' + h + ' FROM local_hashes WHERE hash_id = ?;', ( hash_id, ) ).fetchone()
if result is None:
continue
( archive_hash, ) = result
if hash_type == HydrusTagArchive.HASH_TYPE_MD5: h = 'md5'
elif hash_type == HydrusTagArchive.HASH_TYPE_SHA1: h = 'sha1'
elif hash_type == HydrusTagArchive.HASH_TYPE_SHA512: h = 'sha512'
tag_ids = self._STL( self._c.execute( 'SELECT tag_id FROM ' + current_mappings_table_name + ' WHERE hash_id = ?;', ( hash_id, ) ) )
result = self._c.execute( 'SELECT ' + h + ' FROM local_hashes WHERE hash_id = ?;', ( hash_id, ) ).fetchone()
tags = self._GetTags( tag_ids )
if result is None: continue
hta.AddMappings( archive_hash, tags )
( archive_hash, ) = result
tag_ids = self._c.execute( 'SELECT tag_id FROM ' + current_mappings_table_name + ' WHERE hash_id = ?;', ( hash_id, ) ).fetchall()
tags = self._GetTags( tag_ids )
hta.AddMappings( archive_hash, tags )
job_key.DeleteVariable( 'popup_gauge_1' )
@ -2677,7 +2678,7 @@ class DB( HydrusDB.HydrusDB ):
return hashes_result
def _GetAutocompleteCounts( self, tag_service_id, file_service_id, tag_ids ):
def _GetAutocompleteCounts( self, tag_service_id, file_service_id, tag_ids, include_current, include_pending ):
if tag_service_id == self._combined_tag_service_id:
@ -2708,6 +2709,16 @@ class DB( HydrusDB.HydrusDB ):
for ( tag_id, current_count, pending_count ) in cache_results:
if not include_current:
current_count = 0
if not include_pending:
pending_count = 0
if tag_id in ids_to_count:
( current_min, current_max, pending_min, pending_max ) = ids_to_count[ tag_id ]
@ -2862,7 +2873,7 @@ class DB( HydrusDB.HydrusDB ):
search_tag_service_key = self._GetService( search_tag_service_id ).GetServiceKey()
ids_to_count = self._GetAutocompleteCounts( search_tag_service_id, file_service_id, tag_ids )
ids_to_count = self._GetAutocompleteCounts( search_tag_service_id, file_service_id, tag_ids, include_current, include_pending )
#
@ -3238,7 +3249,6 @@ class DB( HydrusDB.HydrusDB ):
tag_service = self._GetService( tag_service_id )
file_service_type = file_service.GetServiceType()
tag_service_type = tag_service.GetServiceType()
tags_to_include = search_context.GetTagsToInclude()
tags_to_exclude = search_context.GetTagsToExclude()
@ -3410,13 +3420,13 @@ class DB( HydrusDB.HydrusDB ):
if file_service_key == CC.COMBINED_FILE_SERVICE_KEY:
query_hash_ids.intersection_update( [ id for ( id, ) in self._c.execute( 'SELECT hash_id FROM files_info WHERE ' + ' AND '.join( files_info_predicates ) + ';' ) ] )
query_hash_ids.intersection_update( self._STI( self._c.execute( 'SELECT hash_id FROM files_info WHERE ' + ' AND '.join( files_info_predicates ) + ';' ) ) )
else:
files_info_predicates.insert( 0, 'service_id = ' + str( file_service_id ) )
query_hash_ids.intersection_update( [ id for ( id, ) in self._c.execute( 'SELECT hash_id FROM current_files NATURAL JOIN files_info WHERE ' + ' AND '.join( files_info_predicates ) + ';' ) ] )
query_hash_ids.intersection_update( self._STI( self._c.execute( 'SELECT hash_id FROM current_files NATURAL JOIN files_info WHERE ' + ' AND '.join( files_info_predicates ) + ';' ) ) )
@ -3430,13 +3440,13 @@ class DB( HydrusDB.HydrusDB ):
files_info_predicates.insert( 0, 'service_id = ' + str( file_service_id ) )
query_hash_ids = { id for ( id, ) in self._c.execute( 'SELECT hash_id FROM current_files NATURAL JOIN files_info WHERE ' + ' AND '.join( files_info_predicates ) + ';' ) }
query_hash_ids = self._STS( self._c.execute( 'SELECT hash_id FROM current_files NATURAL JOIN files_info WHERE ' + ' AND '.join( files_info_predicates ) + ';' ) )
if file_service_key == CC.COMBINED_LOCAL_FILE_SERVICE_KEY:
repo_update_hash_ids = { id for ( id, ) in self._c.execute( 'SELECT hash_id FROM current_files NATURAL JOIN files_info WHERE service_id = ?;', ( self._local_update_service_id, ) ) }
repo_update_hash_ids = self._STS( self._c.execute( 'SELECT hash_id FROM current_files NATURAL JOIN files_info WHERE service_id = ?;', ( self._local_update_service_id, ) ) )
query_hash_ids.difference_update( repo_update_hash_ids )
@ -3529,7 +3539,7 @@ class DB( HydrusDB.HydrusDB ):
elif must_be_local or must_not_be_local:
local_hash_ids = [ id for ( id, ) in self._c.execute( 'SELECT hash_id FROM current_files WHERE service_id = ?;', ( self._combined_local_file_service_id, ) ) ]
local_hash_ids = self._STL( self._c.execute( 'SELECT hash_id FROM current_files WHERE service_id = ?;', ( self._combined_local_file_service_id, ) ) )
if must_be_local:
@ -3554,7 +3564,6 @@ class DB( HydrusDB.HydrusDB ):
num_tags_zero = False
num_tags_nonzero = False
max_num_tags_exists = False
tag_predicates = []
@ -3894,17 +3903,17 @@ class DB( HydrusDB.HydrusDB ):
if include_current:
for ( id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + current_mappings_table_name + ' GROUP BY hash_id;' ):
for ( hash_id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + current_mappings_table_name + ' GROUP BY hash_id;' ):
tags_counter[ id ] += count
tags_counter[ hash_id ] += count
if include_pending:
for ( id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + pending_mappings_table_name + ' GROUP BY hash_id;' ):
for ( hash_id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + pending_mappings_table_name + ' GROUP BY hash_id;' ):
tags_counter[ id ] += count
tags_counter[ hash_id ] += count
@ -3919,17 +3928,17 @@ class DB( HydrusDB.HydrusDB ):
if include_current:
for ( id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + temp_table_name + ' NATURAL JOIN ' + current_mappings_table_name + ' GROUP BY hash_id;' ):
for ( hash_id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + temp_table_name + ' NATURAL JOIN ' + current_mappings_table_name + ' GROUP BY hash_id;' ):
tags_counter[ id ] += count
tags_counter[ hash_id ] += count
if include_pending:
for ( id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + temp_table_name + ' NATURAL JOIN ' + pending_mappings_table_name + ' GROUP BY hash_id;' ):
for ( hash_id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + temp_table_name + ' NATURAL JOIN ' + pending_mappings_table_name + ' GROUP BY hash_id;' ):
tags_counter[ id ] += count
tags_counter[ hash_id ] += count
@ -3957,29 +3966,15 @@ class DB( HydrusDB.HydrusDB ):
for search_tag_service_id in search_tag_service_ids:
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( search_tag_service_id )
'''
if include_current and include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM hashes WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h ) OR EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
elif include_current:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM hashes WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h );' ) ) )
elif include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM hashes WHERE EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
'''
if include_current:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT DISTINCT hash_id FROM ' + current_mappings_table_name + ';' ) ) )
nonzero_tag_hash_ids.update( self._STI( self._c.execute( 'SELECT DISTINCT hash_id FROM ' + current_mappings_table_name + ';' ) ) )
if include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT DISTINCT hash_id FROM ' + pending_mappings_table_name + ';' ) ) )
nonzero_tag_hash_ids.update( self._STI( self._c.execute( 'SELECT DISTINCT hash_id FROM ' + pending_mappings_table_name + ';' ) ) )
@ -3993,15 +3988,15 @@ class DB( HydrusDB.HydrusDB ):
if include_current and include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM ' + temp_table_name + ' WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h ) OR EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
nonzero_tag_hash_ids.update( self._STI( self._c.execute( 'SELECT hash_id as h FROM ' + temp_table_name + ' WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h ) OR EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
elif include_current:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM ' + temp_table_name + ' WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h );' ) ) )
nonzero_tag_hash_ids.update( self._STI( self._c.execute( 'SELECT hash_id as h FROM ' + temp_table_name + ' WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h );' ) ) )
elif include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM ' + temp_table_name + ' WHERE EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
nonzero_tag_hash_ids.update( self._STI( self._c.execute( 'SELECT hash_id as h FROM ' + temp_table_name + ' WHERE EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
@ -4587,8 +4582,6 @@ class DB( HydrusDB.HydrusDB ):
search_tags = siblings_manager.CollapseTags( service_key, search_tags )
start = HydrusData.GetNowPrecise()
service_id = self._GetServiceId( service_key )
skip_hash_id = self._GetHashId( skip_hash )
@ -7707,7 +7700,7 @@ class DB( HydrusDB.HydrusDB ):
new_inserts = []
for ( service_id, bad_namespace_id, old_subtag_id, new_namespace_id, new_subtag_id, status, reason_id ) in old_data:
for ( service_id, old_namespace_id, old_subtag_id, new_namespace_id, new_subtag_id, status, reason_id ) in old_data:
bad_tag_id = get_tag_id( old_namespace_id, old_subtag_id )
good_tag_id = get_tag_id( new_namespace_id, new_subtag_id )
@ -8189,7 +8182,7 @@ class DB( HydrusDB.HydrusDB ):
if self._SubtagExists( clean_subtag ):
for ( dirty_tag_id, subtag ) in self._c.execute( 'SELECT tag_id, namespace FROM tags NATURAL JOIN namespaces WHERE subtag_id = ?;', ( subtag_id, ) ).fetchall():
for ( dirty_tag_id, namespace ) in self._c.execute( 'SELECT tag_id, namespace FROM tags NATURAL JOIN namespaces WHERE subtag_id = ?;', ( subtag_id, ) ).fetchall():
clean_tag = current_combine_tag( namespace, clean_subtag )
@ -8201,7 +8194,15 @@ class DB( HydrusDB.HydrusDB ):
clean_subtag = 'invalid subtag "' + dirty_subtag + '"'
self._c.execute( 'UPDATE subtags SET subtag = ? WHERE subtag_id = ?;', ( clean_subtag, dirty_subtag_id ) )
try:
self._c.execute( 'UPDATE subtags SET subtag = ? WHERE subtag_id = ?;', ( clean_subtag, dirty_subtag_id ) )
except sqlite3.IntegrityError:
# I'm not sure how this happened--it was on an unusual '-:' -> ':' pair, so it is probably so rare we can skip it for now
invalid_subtag_ids.append( dirty_subtag_id )
for ( dirty_tag_id, clean_tag_id ) in dirty_and_clean_tag_ids:
@ -8258,60 +8259,6 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
def _UpdateImageboards( self, site_edit_log ):
for ( site_action, site_data ) in site_edit_log:
if site_action == HC.ADD:
site_name = site_data
self._GetSiteId( site_name )
elif site_action == HC.DELETE:
site_name = site_data
site_id = self._GetSiteId( site_name )
self._c.execute( 'DELETE FROM imageboard_sites WHERE site_id = ?;', ( site_id, ) )
self._c.execute( 'DELETE FROM imageboards WHERE site_id = ?;', ( site_id, ) )
elif site_action == HC.EDIT:
( site_name, edit_log ) = site_data
site_id = self._GetSiteId( site_name )
for ( action, data ) in edit_log:
if action == HC.ADD:
name = data
imageboard = ClientData.Imageboard( name, '', 60, [], {} )
self._c.execute( 'INSERT INTO imageboards ( site_id, name, imageboard ) VALUES ( ?, ?, ? );', ( site_id, name, imageboard ) )
elif action == HC.DELETE:
name = data
self._c.execute( 'DELETE FROM imageboards WHERE site_id = ? AND name = ?;', ( site_id, name ) )
elif action == HC.EDIT:
imageboard = data
name = imageboard.GetName()
self._c.execute( 'UPDATE imageboards SET imageboard = ? WHERE site_id = ? AND name = ?;', ( imageboard, site_id, name ) )
def _UpdateMappings( self, tag_service_id, mappings_ids = None, deleted_mappings_ids = None, pending_mappings_ids = None, pending_rescinded_mappings_ids = None, petitioned_mappings_ids = None, petitioned_rescinded_mappings_ids = None ):
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( tag_service_id )
@ -8329,10 +8276,6 @@ class DB( HydrusDB.HydrusDB ):
# this cost a lot of CPU time and was extremely difficult to maintain
# now it attempts a simpler union, not letting delete overwrite a current or pending
other_service_ids = [ service_id for service_id in self._GetServiceIds( HC.TAG_SERVICES ) if service_id != tag_service_id ]
splayed_other_service_ids = HydrusData.SplayListForDB( other_service_ids )
change_in_num_mappings = 0
change_in_num_deleted_mappings = 0
change_in_num_pending_mappings = 0
@ -8848,8 +8791,6 @@ class DB( HydrusDB.HydrusDB ):
size = os.path.getsize( db_path )
pretty_size = HydrusData.ConvertIntToBytes( size )
text = 'An attempt to vacuum the database failed.'
text += os.linesep * 2
text += 'For now, automatic vacuuming has been disabled. If the error is not obvious, please contact the hydrus developer.'

View File

@ -1,33 +1,15 @@
import ClientDownloading
import ClientFiles
import ClientThreading
import collections
import hashlib
import httplib
import itertools
import HydrusConstants as HC
import HydrusData
import HydrusExceptions
import HydrusImageHandling
import HydrusNATPunch
import HydrusPaths
import HydrusSerialisable
import HydrusTagArchive
import HydrusTags
import HydrusThreading
import ClientConstants as CC
import os
import Queue
import random
import shutil
import sqlite3
import stat
import sys
import threading
import time
import traceback
import wx
import yaml
def DAEMONCheckExportFolders( controller ):

View File

@ -1,32 +1,23 @@
import ClientConstants as CC
import ClientDefaults
import ClientDownloading
import ClientFiles
import ClientNetworking
import ClientThreading
import collections
import HydrusConstants as HC
import HydrusData
import HydrusExceptions
import HydrusFileHandling
import HydrusGlobals
import HydrusPaths
import HydrusSerialisable
import HydrusTags
import threading
import traceback
import os
import random
import requests
import shutil
import sqlite3
import stat
import sys
import time
import urllib
import wx
import yaml
import HydrusData
import HydrusGlobals
import HydrusThreading
def AddPaddingToDimensions( dimensions, padding ):
@ -557,6 +548,8 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
self._dictionary[ 'booleans' ][ 'add_parents_on_manage_tags' ] = True
self._dictionary[ 'booleans' ][ 'replace_siblings_on_manage_tags' ] = True
self._dictionary[ 'booleans' ][ 'get_tags_if_url_known_and_file_redundant' ] = False
self._dictionary[ 'booleans' ][ 'show_related_tags' ] = False
self._dictionary[ 'booleans' ][ 'show_file_lookup_script_tags' ] = False
self._dictionary[ 'booleans' ][ 'hide_message_manager_on_gui_iconise' ] = HC.PLATFORM_OSX
@ -1464,11 +1457,11 @@ class ImportTagOptions( HydrusSerialisable.SerialisableBase ):
return service_keys_to_content_updates
def ShouldFetchTags( self ):
def InterestedInTags( self ):
i_am_interested_in_namespaces = len( self._service_keys_to_namespaces ) > 0
i_am_interested = len( self._service_keys_to_namespaces ) > 0
return i_am_interested_in_namespaces
return i_am_interested
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_TAG_OPTIONS ] = ImportTagOptions

View File

@ -1,26 +1,17 @@
import bs4
import ClientNetworking
import collections
import httplib
import HydrusConstants as HC
import HydrusExceptions
import HydrusPaths
import HydrusSerialisable
import HydrusThreading
import json
import lxml # to force import for later bs4 stuff
import os
import pafy
import re
import requests
import sys
import threading
import time
import traceback
import urllib
import urlparse
import wx
import HydrusTags
import HydrusData
import ClientConstants as CC
import HydrusGlobals

View File

@ -1,5 +1,3 @@
import ClientFiles
import json
import wx
class FileDropTarget( wx.PyDropTarget ):
@ -58,4 +56,4 @@ class FileDropTarget( wx.PyDropTarget ):
return wx.DragCopy

View File

@ -1,6 +1,5 @@
import ClientConstants as CC
import ClientData
import ClientFiles
import ClientSearch
import HydrusConstants as HC
import HydrusData

View File

@ -1,20 +1,5 @@
import ClientConstants as CC
import ClientData
import gc
import HydrusConstants as HC
import HydrusData
import HydrusExceptions
import HydrusFileHandling
import HydrusGlobals
import HydrusPaths
import HydrusSerialisable
import itertools
import os
import random
import re
import shutil
import stat
import wx
def GetAllPaths( raw_paths ):

View File

@ -1,9 +1,6 @@
import httplib
import HydrusConstants as HC
import ClientConstants as CC
import ClientCaches
import ClientFiles
import ClientData
import ClientDragDrop
import ClientExporting
import ClientGUICommon
@ -28,21 +25,15 @@ import gc
import hashlib
import HydrusData
import HydrusExceptions
import HydrusFileHandling
import HydrusPaths
import HydrusGlobals
import HydrusImageHandling
import HydrusNATPunch
import HydrusNetwork
import HydrusNetworking
import HydrusSerialisable
import HydrusTagArchive
import HydrusThreading
import HydrusVideoHandling
import itertools
import os
import PIL
import random
import sqlite3
import ssl
import subprocess
@ -53,7 +44,6 @@ import traceback
import types
import webbrowser
import wx
import yaml
# Sizer Flags
@ -116,7 +106,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self._controller.sub( self, 'NewPageImportURLs', 'new_page_import_urls' )
self._controller.sub( self, 'NewPagePetitions', 'new_page_petitions' )
self._controller.sub( self, 'NewPageQuery', 'new_page_query' )
self._controller.sub( self, 'NewPageThreadDumper', 'new_thread_dumper' )
self._controller.sub( self, 'NewSimilarTo', 'new_similar_to' )
self._controller.sub( self, 'NotifyNewOptions', 'notify_new_options' )
self._controller.sub( self, 'NotifyNewPending', 'notify_new_pending' )
@ -897,11 +886,13 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
def file():
ClientGUIMenus.AppendMenuItem( self, menu, 'import files', 'Add new files to the database.', self._ImportFiles )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage import folders', 'Manage folders from which the client can automatically import.', self._ManageImportFolders )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage export folders', 'Manage folders to which the client can automatically export.', self._ManageExportFolders )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
open = wx.Menu()
@ -911,11 +902,11 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenu( menu, open, 'open' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'options', 'Change how the client operates.', self._ManageOptions )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
we_borked_linux_pyinstaller = HC.PLATFORM_LINUX and not HC.RUNNING_FROM_SOURCE
@ -946,28 +937,19 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
show = True
did_undo_stuff = False
if undo_string is not None:
did_undo_stuff = True
ClientGUIMenus.AppendMenuItem( self, menu, undo_string, 'Undo last operation.', self._controller.pub, 'undo' )
if redo_string is not None:
did_undo_stuff = True
ClientGUIMenus.AppendMenuItem( self, menu, redo_string, 'Redo last operation.', self._controller.pub, 'redo' )
if have_closed_pages:
if did_undo_stuff:
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
undo_pages = wx.Menu()
@ -1008,7 +990,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, menu, 'refresh', 'If the current page has a search, refresh it.', self._Refresh )
ClientGUIMenus.AppendMenuItem( self, menu, 'show/hide management and preview panels', 'Show or hide the panels on the left.', self._ShowHideSplitters )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
gui_session_names = self._controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION )
@ -1045,7 +1027,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenu( menu, sessions, 'sessions' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'pick a new page', 'Choose a new page to open.', self._ChooseNewPage )
@ -1071,7 +1053,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, search_menu, service.GetName(), 'Open a new search tab for ' + service.GetName() + '.', self._NewPageQuery, service.GetServiceKey() )
search_menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( search_menu )
ClientGUIMenus.AppendMenuItem( self, search_menu, 'duplicates (under construction!)', 'Open a new tab to discover and filter duplicate files.', self._NewPageDuplicateFilter )
@ -1152,12 +1134,12 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, menu, 'set a password', 'Set a simple password for the database so only you can open it in the client.', self._SetPassword )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'create a database backup', 'Back the database up to an external location.', self._controller.BackupDatabase )
ClientGUIMenus.AppendMenuItem( self, menu, 'restore a database backup', 'Restore the database from an external location.', self._controller.RestoreDatabase )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
submenu = wx.Menu()
@ -1272,7 +1254,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenu( menu, submenu, 'pause' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'review services', 'Look at the services your client connects to.', self._ReviewServices )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage services', 'Edit the services your client connects to.', self._ManageServices )
@ -1317,7 +1299,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
if can_overrule_account_types:
submenu.AppendSeparator()
ClientGUIMenus.AppendSeparator( submenu )
ClientGUIMenus.AppendMenuItem( self, submenu, 'manage account types', 'Add, edit and delete account types for this service.', self._ManageAccountTypes, service_key )
@ -1348,7 +1330,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
if can_overrule_account_types:
submenu.AppendSeparator()
ClientGUIMenus.AppendSeparator( submenu )
ClientGUIMenus.AppendMenuItem( self, submenu, 'manage account types', 'Add, edit and delete account types for this service.', self._ManageAccountTypes, service_key )
@ -1357,7 +1339,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
if can_overrule_services:
submenu.AppendSeparator()
ClientGUIMenus.AppendSeparator( submenu )
ClientGUIMenus.AppendMenuItem( self, submenu, 'manage services', 'Add, edit, and delete this server\'s services.', self._ManageServer, service_key )
ClientGUIMenus.AppendMenuItem( self, submenu, 'make a backup', 'Command the server to temporarily pause and back up its database.', self._BackupService, service_key )
@ -1369,27 +1351,27 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenu( menu, admin_menu, 'administrate services' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'import repository update files', 'Add repository update files to the database.', self._ImportUpdateFiles )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage tag censorship', 'Set which tags you want to see from which services.', self._ManageTagCensorship )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage tag siblings', 'Set certain tags to be automatically replaced with other tags.', self._ManageTagSiblings )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage tag parents', 'Set certain tags to be automatically added with other tags.', self._ManageTagParents )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage parsing scripts', 'Manage how the client parses different types of web content.', self._ManageParsingScripts )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage boorus', 'Change the html parsing information for boorus to download from.', self._ManageBoorus )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage pixiv account', 'Set up your pixiv username and password.', self._ManagePixivAccount )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage subscriptions', 'Change the queries you want the client to regularly import from.', self._ManageSubscriptions )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage upnp', 'If your router supports it, see and edit your current UPnP NAT traversal mappings.', self._ManageUPnP )
@ -3117,19 +3099,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
self._NewPageQuery( service_key, initial_media_results = initial_media_results, initial_predicates = initial_predicates )
def NewPageThreadDumper( self, hashes ):
with ClientGUIDialogs.DialogSelectImageboard( self ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
imageboard = dlg.GetImageboard()
pass
def NewSimilarTo( self, file_service_key, hash, hamming_distance ):
initial_predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, ( hash, hamming_distance ) ) ]
@ -3326,8 +3295,7 @@ class FrameSplash( wx.Frame ):
self.Show( True )
self._controller.sub( self, 'SetTitleText', 'splash_set_title_text' )
self._controller.sub( self, 'SetStatusText', 'splash_set_status_text' )
self._controller.sub( self, 'SetStatusTextNoLog', 'splash_set_status_text_no_log' )
self._controller.sub( self, 'SetText', 'splash_set_status_text' )
self._controller.sub( self, 'Destroy', 'splash_destroy' )
self.Raise()
@ -3415,7 +3383,7 @@ class FrameSplash( wx.Frame ):
def SetStatusText( self, text, print_to_log = True ):
def SetText( self, text, print_to_log = True ):
if print_to_log:

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,14 @@
import collections
import HydrusConstants as HC
import ClientCaches
import ClientData
import ClientConstants as CC
import ClientGUIMenus
import ClientRatings
import ClientThreading
import itertools
import HydrusConstants as HC
import HydrusData
import HydrusExceptions
import HydrusGlobals
import os
import random
import sys
import threading
import time
@ -19,10 +19,6 @@ import wx.richtext
import wx.lib.newevent
from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin
from wx.lib.mixins.listctrl import ColumnSorterMixin
import HydrusTags
import HydrusData
import HydrusExceptions
import HydrusGlobals
TEXT_CUTOFF = 1024
@ -655,17 +651,17 @@ class ExportPatternButton( wx.Button ):
menu.Append( -1, 'click on a phrase to copy to clipboard' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
menu.Append( self.ID_HASH, 'the file\'s hash - {hash}' )
menu.Append( self.ID_TAGS, 'all the file\'s tags - {tags}' )
menu.Append( self.ID_NN_TAGS, 'all the file\'s non-namespaced tags - {nn tags}' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
menu.Append( self.ID_NAMESPACE, u'all instances of a particular namespace - [\u2026]' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
menu.Append( self.ID_TAG, u'a particular tag, if the file has it - (\u2026)' )
@ -1207,6 +1203,61 @@ class ListCtrlAutoWidth( wx.ListCtrl, ListCtrlAutoWidthMixin ):
for index in indices: self.DeleteItem( index )
class CheckboxManager( object ):
def GetCurrentValue( self ):
raise NotImplementedError()
def Invert( self ):
raise NotImplementedError()
class CheckboxManagerCalls( CheckboxManager ):
def __init__( self, invert_call, value_call ):
CheckboxManager.__init__( self )
self._invert_call = invert_call
self._value_call = value_call
def GetCurrentValue( self ):
return self._value_call()
def Invert( self ):
self._invert_call()
class CheckboxManagerOptions( CheckboxManager ):
def __init__( self, boolean_name ):
CheckboxManager.__init__( self )
self._boolean_name = boolean_name
def GetCurrentValue( self ):
new_options = HydrusGlobals.client_controller.GetNewOptions()
return new_options.GetBoolean( self._boolean_name )
def Invert( self ):
new_options = HydrusGlobals.client_controller.GetNewOptions()
new_options.InvertBoolean( self._boolean_name )
class MenuBitmapButton( BetterBitmapButton ):
def __init__( self, parent, bitmap, menu_items ):
@ -1216,13 +1267,6 @@ class MenuBitmapButton( BetterBitmapButton ):
self._menu_items = menu_items
def _DoBooloanCheck( self, boolean_name ):
new_options = HydrusGlobals.client_controller.GetNewOptions()
new_options.InvertBoolean( boolean_name )
def DoMenu( self ):
menu = wx.Menu()
@ -1231,23 +1275,22 @@ class MenuBitmapButton( BetterBitmapButton ):
if item_type == 'normal':
callable = data
func = data
ClientGUIMenus.AppendMenuItem( self, menu, title, description, callable )
ClientGUIMenus.AppendMenuItem( self, menu, title, description, func )
elif item_type == 'check':
new_options = HydrusGlobals.client_controller.GetNewOptions()
check_manager = data
boolean_name = data
current_value = check_manager.GetCurrentValue()
func = check_manager.Invert
initial_value = new_options.GetBoolean( boolean_name )
ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, initial_value, self._DoBooloanCheck, boolean_name )
ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, current_value, func )
elif item_type == 'separator':
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
@ -1263,13 +1306,6 @@ class MenuButton( BetterButton ):
self._menu_items = menu_items
def _DoBooloanCheck( self, boolean_name ):
new_options = HydrusGlobals.client_controller.GetNewOptions()
new_options.InvertBoolean( boolean_name )
def DoMenu( self ):
menu = wx.Menu()
@ -1284,17 +1320,15 @@ class MenuButton( BetterButton ):
elif item_type == 'check':
new_options = HydrusGlobals.client_controller.GetNewOptions()
check_manager = data
boolean_name = data
initial_value = check_manager.GetInitialValue()
initial_value = new_options.GetBoolean( boolean_name )
ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, initial_value, self._DoBooloanCheck, boolean_name )
ClientGUIMenus.AppendMenuCheckItem( self, menu, title, description, initial_value, check_manager.Invert )
elif item_type == 'separator':
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
elif item_type == 'label':
@ -1534,6 +1568,8 @@ class PopupDismissAll( PopupWindow ):
class PopupMessage( PopupWindow ):
WRAP_WIDTH = 400
def __init__( self, parent, job_key ):
PopupWindow.__init__( self, parent )
@ -1547,20 +1583,20 @@ class PopupMessage( PopupWindow ):
self._title.Hide()
self._text_1 = FitResistantStaticText( self )
self._text_1.Wrap( 380 )
self._text_1.Wrap( self.WRAP_WIDTH )
self._text_1.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._text_1.Hide()
self._gauge_1 = Gauge( self, size = ( 380, -1 ) )
self._gauge_1 = Gauge( self, size = ( self.WRAP_WIDTH, -1 ) )
self._gauge_1.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._gauge_1.Hide()
self._text_2 = FitResistantStaticText( self )
self._text_2.Wrap( 380 )
self._text_2.Wrap( self.WRAP_WIDTH )
self._text_2.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._text_2.Hide()
self._gauge_2 = Gauge( self, size = ( 380, -1 ) )
self._gauge_2 = Gauge( self, size = ( self.WRAP_WIDTH, -1 ) )
self._gauge_2.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._gauge_2.Hide()
@ -1581,7 +1617,7 @@ class PopupMessage( PopupWindow ):
self._show_tb_button.Hide()
self._tb_text = FitResistantStaticText( self )
self._tb_text.Wrap( 380 )
self._tb_text.Wrap( self.WRAP_WIDTH )
self._tb_text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._tb_text.Hide()
@ -2869,7 +2905,7 @@ class RegexButton( wx.Button ):
menu.Append( -1, 'click on a phrase to copy to clipboard' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
submenu = wx.Menu()
@ -2883,7 +2919,7 @@ class RegexButton( wx.Button ):
submenu.Append( self.ID_REGEX_SET, u'any of these - [\u2026]' )
submenu.Append( self.ID_REGEX_NOT_SET, u'anything other than these - [^\u2026]' )
submenu.AppendSeparator()
ClientGUIMenus.AppendSeparator( submenu )
submenu.Append( self.ID_REGEX_0_OR_MORE_GREEDY, r'0 or more matches, consuming as many as possible - *' )
submenu.Append( self.ID_REGEX_1_OR_MORE_GREEDY, r'1 or more matches, consuming as many as possible - +' )
@ -2895,17 +2931,17 @@ class RegexButton( wx.Button ):
submenu.Append( self.ID_REGEX_M_TO_N_GREEDY, r'm to n matches, consuming as many as possible - {m,n}' )
submenu.Append( self.ID_REGEX_M_TO_N_MINIMAL, r'm to n matches, consuming as few as possible - {m,n}?' )
submenu.AppendSeparator()
ClientGUIMenus.AppendSeparator( submenu )
submenu.Append( self.ID_REGEX_LOOKAHEAD, u'the next characters are: (non-consuming) - (?=\u2026)' )
submenu.Append( self.ID_REGEX_NEGATIVE_LOOKAHEAD, u'the next characters are not: (non-consuming) - (?!\u2026)' )
submenu.Append( self.ID_REGEX_LOOKBEHIND, u'the previous characters are: (non-consuming) - (?<=\u2026)' )
submenu.Append( self.ID_REGEX_NEGATIVE_LOOKBEHIND, u'the previous characters are not: (non-consuming) - (?<!\u2026)' )
submenu.AppendSeparator()
ClientGUIMenus.AppendSeparator( submenu )
submenu.Append( self.ID_REGEX_NUMBER_WITHOUT_ZEROES, r'0074 -> 74 - [1-9]+\d*' )
submenu.Append( self.ID_REGEX_FILENAME, r'filename - (?<=' + os.path.sep.encode( 'string_escape' ) + r')[^' + os.path.sep.encode( 'string_escape' ) + ']*?(?=\..*$)' )
submenu.Append( self.ID_REGEX_FILENAME, r'filename - (?<=' + os.path.sep.encode( 'string_escape' ) + r')[^' + os.path.sep.encode( 'string_escape' ) + r']*?(?=\..*$)' )
menu.AppendMenu( -1, 'regex components', submenu )
@ -2913,7 +2949,7 @@ class RegexButton( wx.Button ):
submenu.Append( self.ID_REGEX_MANAGE_FAVOURITES, 'manage favourites' )
submenu.AppendSeparator()
ClientGUIMenus.AppendSeparator( submenu )
for ( index, ( regex_phrase, description ) ) in enumerate( HC.options[ 'regex_favourites' ] ):
@ -2956,7 +2992,7 @@ class RegexButton( wx.Button ):
elif id == self.ID_REGEX_LOOKBEHIND: phrase = u'(?<=\u2026)'
elif id == self.ID_REGEX_NEGATIVE_LOOKBEHIND: phrase = u'(?<!\u2026)'
elif id == self.ID_REGEX_NUMBER_WITHOUT_ZEROES: phrase = r'[1-9]+\d*'
elif id == self.ID_REGEX_FILENAME: phrase = '(?<=' + os.path.sep.encode( 'string_escape' ) + r')[^' + os.path.sep.encode( 'string_escape' ) + ']*?(?=\..*$)'
elif id == self.ID_REGEX_FILENAME: phrase = '(?<=' + os.path.sep.encode( 'string_escape' ) + r')[^' + os.path.sep.encode( 'string_escape' ) + r']*?(?=\..*$)'
elif id == self.ID_REGEX_MANAGE_FAVOURITES:
import ClientGUIDialogsManage

View File

@ -2,6 +2,7 @@ import ClientCaches
import ClientConstants as CC
import ClientGUICommon
import ClientGUIDialogs
import ClientGUIMenus
import ClientGUIScrolledPanels
import ClientGUITopLevelWindows
import HydrusConstants as HC
@ -367,7 +368,7 @@ class SeedCacheControl( ClientGUICommon.SaneListCtrlForSingleObject ):
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_seeds' ), 'copy sources' )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_seed_notes' ), 'copy notes' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'set_seed_unknown' ), 'try again' )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'set_seed_skipped' ), 'skip' )

View File

@ -806,9 +806,9 @@ class DialogGenerateNewAccounts( Dialog ):
self._account_types.Select( 0 )
for ( str, value ) in HC.lifetimes:
for ( s, value ) in HC.lifetimes:
self._lifetime.Append( str, value )
self._lifetime.Append( s, value )
self._lifetime.SetSelection( 3 ) # one year
@ -864,7 +864,12 @@ class DialogGenerateNewAccounts( Dialog ):
try:
request_args = { 'num' : num, 'account_type_key' : account_type_key, 'expires' : expires }
request_args = { 'num' : num, 'account_type_key' : account_type_key }
if expires is not None:
request_args[ 'expires' ] = expires
response = service.Request( HC.GET, 'registration_keys', request_args )
@ -1999,7 +2004,7 @@ class DialogInputNamespaceRegex( Dialog ):
vbox = wx.BoxSizer( wx.VERTICAL )
intro = 'Put the namespace (e.g. page) on the left.' + os.linesep + 'Put the regex (e.g. [1-9]+\d*(?=.{4}$)) on the right.' + os.linesep + 'All files will be tagged with "namespace:regex".'
intro = r'Put the namespace (e.g. page) on the left.' + os.linesep + r'Put the regex (e.g. [1-9]+\d*(?=.{4}$)) on the right.' + os.linesep + r'All files will be tagged with "namespace:regex".'
vbox.AddF( wx.StaticText( self, label = intro ), CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( control_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
@ -2992,6 +2997,12 @@ class DialogPathsToTags( Dialog ):
tags = HydrusTags.CleanTags( tags )
siblings_manager = HydrusGlobals.client_controller.GetManager( 'tag_siblings' )
parents_manager = HydrusGlobals.client_controller.GetManager( 'tag_parents' )
tags = siblings_manager.CollapseTags( self._service_key, tags )
tags = parents_manager.ExpandTags( self._service_key, tags )
tags = list( tags )
tags.sort()

View File

@ -3449,7 +3449,7 @@ class DialogManageTagCensorship( ClientGUIDialogs.Dialog ):
vbox = wx.BoxSizer( wx.VERTICAL )
intro = "Here you can set which tags or classes of tags you do not want to see. Input something like 'series:' to censor an entire namespace, or ':' for all namespaced tags, and '' for all unnamespaced tags. You may have to refresh your current queries to see any changes."
intro = "Here you can set which tags or classes of tags you do not want to see. Input something like 'series:' to censor an entire namespace, or ':' for all namespaced tags, and the empty string (just hit enter with no text added) for all unnamespaced tags. You will have to refresh your current queries to see any changes."
st = wx.StaticText( self, label = intro )

View File

@ -1,6 +1,7 @@
import ClientCaches
import ClientConstants as CC
import ClientData
import ClientGUIMenus
import ClientSearch
import collections
import HydrusConstants as HC
@ -741,21 +742,15 @@ class ListBoxTags( ListBox ):
if menu.GetMenuItemCount() > 0:
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
if self.can_spawn_new_windows:
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'new_search_page' ), 'open a new search page for ' + selection_string )
if menu.GetMenuItemCount() > 0:
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_terms' ), 'copy ' + selection_string )
@ -776,13 +771,10 @@ class ListBoxTags( ListBox ):
ClientGUIMenus.AppendSeparator( menu )
if len( self._ordered_strings ) > len( self._selected_terms ):
if menu.GetMenuItemCount() > 0:
menu.AppendSeparator()
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_all_tags' ), 'copy all tags' )
if self.has_counts: menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_all_tags_with_counts' ), 'copy all tags with counts' )
@ -793,10 +785,7 @@ class ListBoxTags( ListBox ):
if str in term_types or unicode in term_types:
if menu.GetMenuItemCount() > 0:
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
if len( self._selected_terms ) == 1:

View File

@ -730,7 +730,10 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
menu_items.append( ( 'normal', 'refresh', 'This panel does not update itself when files are added or deleted elsewhere in the client. Hitting this will refresh the numbers from the database.', self._RefreshAndUpdateStatus ) )
menu_items.append( ( 'normal', 'reset potential duplicates', 'This will delete all the potential duplicate pairs found so far and reset their files\' search status.', self._ResetUnknown ) )
menu_items.append( ( 'separator', 0, 0, 0 ) )
menu_items.append( ( 'check', 'search for duplicate pairs at the current distance during normal db maintenance', 'Tell the client to find duplicate pairs in its normal db maintenance cycles, whether you have that set to idle or shutdown time.', 'maintain_similar_files_duplicate_pairs_during_idle' ) )
check_manager = ClientGUICommon.CheckboxManagerOptions( 'maintain_similar_files_duplicate_pairs_during_idle' )
menu_items.append( ( 'check', 'search for duplicate pairs at the current distance during normal db maintenance', 'Tell the client to find duplicate pairs in its normal db maintenance cycles, whether you have that set to idle or shutdown time.', check_manager ) )
self._cog_button = ClientGUICommon.MenuBitmapButton( self, CC.GlobalBMPs.cog, menu_items )
@ -1159,6 +1162,8 @@ class ManagementPanelGalleryImport( ManagementPanel ):
ManagementPanel.__init__( self, parent, page, controller, management_controller )
self._gallery_import = self._management_controller.GetVariable( 'gallery_import' )
self._gallery_downloader_panel = ClientGUICommon.StaticBox( self, 'gallery downloader' )
self._import_queue_panel = ClientGUICommon.StaticBox( self._gallery_downloader_panel, 'imports' )
@ -1206,9 +1211,22 @@ class ManagementPanelGalleryImport( ManagementPanel ):
self._query_paste = wx.Button( self._pending_queries_panel, label = 'paste queries' )
self._query_paste.Bind( wx.EVT_BUTTON, self.EventPaste )
self._get_tags_if_redundant = wx.CheckBox( self._gallery_downloader_panel, label = 'get tags even if file is already in db' )
self._get_tags_if_redundant.Bind( wx.EVT_CHECKBOX, self.EventGetTagsIfRedundant )
self._get_tags_if_redundant.SetToolTipString( 'if off, the downloader will only fetch tags from the gallery if the file is new' )
menu_items = []
invert_call = self._gallery_import.InvertGetTagsIfURLKnownAndFileRedundant
value_call = self._gallery_import.GetTagsIfURLKnownAndFileRedundant
check_manager = ClientGUICommon.CheckboxManagerCalls( invert_call, value_call )
menu_items.append( ( 'check', 'get tags even if url is known and file is already in db (this downloader)', 'If this is selected, the client will fetch the tags from a file\'s page even if it has the file and already previously downloaded it from that location.', check_manager ) )
menu_items.append( ( 'separator', 0, 0, 0 ) )
check_manager = ClientGUICommon.CheckboxManagerOptions( 'get_tags_if_url_known_and_file_redundant' )
menu_items.append( ( 'check', 'get tags even if url is known and file is already in db (default)', 'Set the default for this value.', check_manager ) )
self._cog_button = ClientGUICommon.MenuBitmapButton( self._gallery_downloader_panel, CC.GlobalBMPs.cog, menu_items )
self._file_limit = ClientGUICommon.NoneableSpinCtrl( self._gallery_downloader_panel, 'stop after this many files', min = 1, none_phrase = 'no limit' )
self._file_limit.Bind( wx.EVT_SPINCTRL, self.EventFileLimit )
@ -1265,7 +1283,7 @@ class ManagementPanelGalleryImport( ManagementPanel ):
self._gallery_downloader_panel.AddF( self._import_queue_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
self._gallery_downloader_panel.AddF( self._gallery_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
self._gallery_downloader_panel.AddF( self._pending_queries_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
self._gallery_downloader_panel.AddF( self._get_tags_if_redundant, CC.FLAGS_EXPAND_PERPENDICULAR )
self._gallery_downloader_panel.AddF( self._cog_button, CC.FLAGS_LONE_BUTTON )
self._gallery_downloader_panel.AddF( self._file_limit, CC.FLAGS_EXPAND_PERPENDICULAR )
self._gallery_downloader_panel.AddF( self._import_file_options, CC.FLAGS_EXPAND_PERPENDICULAR )
self._gallery_downloader_panel.AddF( self._import_tag_options, CC.FLAGS_EXPAND_PERPENDICULAR )
@ -1288,8 +1306,6 @@ class ManagementPanelGalleryImport( ManagementPanel ):
self._controller.sub( self, 'UpdateStatus', 'update_status' )
self._gallery_import = self._management_controller.GetVariable( 'gallery_import' )
gallery_identifier = self._gallery_import.GetGalleryIdentifier()
( namespaces, search_value ) = ClientDefaults.GetDefaultNamespacesAndSearchValue( gallery_identifier )
@ -1305,12 +1321,11 @@ class ManagementPanelGalleryImport( ManagementPanel ):
self._gallery_import.SetDownloadHook( file_download_hook )
( import_file_options, import_tag_options, get_tags_if_redundant, file_limit ) = self._gallery_import.GetOptions()
( import_file_options, import_tag_options, file_limit ) = self._gallery_import.GetOptions()
self._import_file_options.SetOptions( import_file_options )
self._import_tag_options.SetOptions( import_tag_options )
self._get_tags_if_redundant.SetValue( get_tags_if_redundant )
self._file_limit.SetValue( file_limit )
self._Update()
@ -1480,11 +1495,6 @@ class ManagementPanelGalleryImport( ManagementPanel ):
self._Update()
def EventGetTagsIfRedundant( self, event ):
self._gallery_import.SetGetTagsIfRedundant( self._get_tags_if_redundant.GetValue() )
def EventKeyDown( self, event ):
if event.KeyCode in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
@ -2130,21 +2140,54 @@ class ManagementPanelPetitions( ManagementPanel ):
self._service = self._controller.GetServicesManager().GetService( self._petition_service_key )
self._can_ban = self._service.HasPermission( HC.CONTENT_TYPE_ACCOUNTS, HC.PERMISSION_ACTION_OVERRULE )
self._num_petitions = None
service_type = self._service.GetServiceType()
self._num_petition_info = None
self._current_petition = None
#
self._petitions_info_panel = ClientGUICommon.StaticBox( self, 'petitions info' )
self._num_petitions_text = wx.StaticText( self._petitions_info_panel )
self._refresh_num_petitions_button = ClientGUICommon.BetterButton( self._petitions_info_panel, 'refresh counts', self._FetchNumPetitions )
refresh_num_petitions = wx.Button( self._petitions_info_panel, label = 'refresh' )
refresh_num_petitions.Bind( wx.EVT_BUTTON, self.EventRefreshNumPetitions )
self._petition_types_to_controls = {}
self._get_petition = wx.Button( self._petitions_info_panel, label = 'get petition' )
self._get_petition.Bind( wx.EVT_BUTTON, self.EventGetPetition )
self._get_petition.Disable()
content_type_hboxes = []
petition_types = []
if service_type == HC.FILE_REPOSITORY:
petition_types.append( ( HC.CONTENT_TYPE_FILES, HC.CONTENT_STATUS_PETITIONED ) )
elif service_type == HC.TAG_REPOSITORY:
petition_types.append( ( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_STATUS_PETITIONED ) )
petition_types.append( ( HC.CONTENT_TYPE_TAG_PARENTS, HC.CONTENT_STATUS_PENDING ) )
petition_types.append( ( HC.CONTENT_TYPE_TAG_PARENTS, HC.CONTENT_STATUS_PETITIONED ) )
petition_types.append( ( HC.CONTENT_TYPE_TAG_SIBLINGS, HC.CONTENT_STATUS_PENDING ) )
petition_types.append( ( HC.CONTENT_TYPE_TAG_SIBLINGS, HC.CONTENT_STATUS_PETITIONED ) )
for ( content_type, status ) in petition_types:
func = HydrusData.Call( self._FetchPetition, content_type, status )
st = wx.StaticText( self._petitions_info_panel )
button = ClientGUICommon.BetterButton( self._petitions_info_panel, 'fetch ' + HC.content_status_string_lookup[ status ] + ' ' + HC.content_type_string_lookup[ content_type ] + ' petition', func )
button.Disable()
self._petition_types_to_controls[ ( content_type, status ) ] = ( st, button )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( st, CC.FLAGS_VCENTER_EXPAND_DEPTH_ONLY )
hbox.AddF( button, CC.FLAGS_VCENTER )
content_type_hboxes.append( hbox )
#
@ -2155,6 +2198,9 @@ class ManagementPanelPetitions( ManagementPanel ):
self._reason_text = ClientGUICommon.SaneMultilineTextCtrl( self._petition_panel, style = wx.TE_READONLY )
self._reason_text.SetMinSize( ( -1, 80 ) )
check_all = ClientGUICommon.BetterButton( self._petition_panel, 'check all', self._CheckAll )
check_none = ClientGUICommon.BetterButton( self._petition_panel, 'check none', self._CheckNone )
self._contents = wx.CheckListBox( self._petition_panel, size = ( -1, 300 ) )
self._contents.Bind( wx.EVT_LISTBOX_DCLICK, self.EventContentDoubleClick )
@ -2170,17 +2216,22 @@ class ManagementPanelPetitions( ManagementPanel ):
#
num_petitions_hbox = wx.BoxSizer( wx.HORIZONTAL )
self._petitions_info_panel.AddF( self._refresh_num_petitions_button, CC.FLAGS_EXPAND_PERPENDICULAR )
num_petitions_hbox.AddF( self._num_petitions_text, CC.FLAGS_EXPAND_BOTH_WAYS )
num_petitions_hbox.AddF( refresh_num_petitions, CC.FLAGS_VCENTER )
for hbox in content_type_hboxes:
self._petitions_info_panel.AddF( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._petitions_info_panel.AddF( num_petitions_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._petitions_info_panel.AddF( self._get_petition, CC.FLAGS_EXPAND_PERPENDICULAR )
check_hbox = wx.BoxSizer( wx.HORIZONTAL )
self._petition_panel.AddF( wx.StaticText( self._petition_panel, label = 'Double click a petition to see its files, if it has them.' ), CC.FLAGS_EXPAND_PERPENDICULAR )
check_hbox.AddF( check_all, CC.FLAGS_VCENTER_EXPAND_DEPTH_ONLY )
check_hbox.AddF( check_none, CC.FLAGS_VCENTER_EXPAND_DEPTH_ONLY )
self._petition_panel.AddF( wx.StaticText( self._petition_panel, label = 'Double-click a petition to see its files, if it has them.' ), CC.FLAGS_EXPAND_PERPENDICULAR )
self._petition_panel.AddF( self._action_text, CC.FLAGS_EXPAND_PERPENDICULAR )
self._petition_panel.AddF( self._reason_text, CC.FLAGS_EXPAND_PERPENDICULAR )
self._petition_panel.AddF( check_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._petition_panel.AddF( self._contents, CC.FLAGS_EXPAND_BOTH_WAYS )
self._petition_panel.AddF( self._process, CC.FLAGS_EXPAND_PERPENDICULAR )
self._petition_panel.AddF( self._modify_petitioner, CC.FLAGS_EXPAND_PERPENDICULAR )
@ -2197,11 +2248,27 @@ class ManagementPanelPetitions( ManagementPanel ):
self.SetSizer( vbox )
wx.CallAfter( self.EventRefreshNumPetitions, None )
wx.CallAfter( self._FetchNumPetitions )
self._controller.sub( self, 'RefreshQuery', 'refresh_query' )
def _CheckAll( self ):
for i in range( self._contents.GetCount() ):
self._contents.Check( i )
def _CheckNone( self ):
for i in range( self._contents.GetCount() ):
self._contents.Check( i, False )
def _DrawCurrentPetition( self ):
hashes = []
@ -2210,6 +2277,7 @@ class ManagementPanelPetitions( ManagementPanel ):
self._action_text.SetLabelText( '' )
self._reason_text.SetValue( '' )
self._reason_text.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOW ) )
self._contents.Clear()
self._process.Disable()
@ -2229,6 +2297,8 @@ class ManagementPanelPetitions( ManagementPanel ):
self._reason_text.SetValue( reason )
self._reason_text.SetBackgroundColour( action_colour )
contents = self._current_petition.GetContents()
self._contents.Clear()
@ -2251,6 +2321,96 @@ class ManagementPanelPetitions( ManagementPanel ):
self._ShowHashes( [] )
def _DrawNumPetitions( self ):
new_petition_fetched = False
for ( content_type, status, count ) in self._num_petition_info:
petition_type = ( content_type, status )
if petition_type in self._petition_types_to_controls:
( st, button ) = self._petition_types_to_controls[ petition_type ]
st.SetLabelText( HydrusData.ConvertIntToPrettyString( count ) + ' petitions' )
if count > 0:
button.Enable()
if self._current_petition is None and not new_petition_fetched:
self._FetchPetition( content_type, status )
new_petition_fetched = True
else:
button.Disable()
def _FetchNumPetitions( self ):
def do_it():
try:
response = self._service.Request( HC.GET, 'num_petitions' )
self._num_petition_info = response[ 'num_petitions' ]
wx.CallAfter( self._DrawNumPetitions )
finally:
self._refresh_num_petitions_button.SetLabelText( 'refresh counts' )
self._refresh_num_petitions_button.SetLabelText( u'Fetching\u2026' )
self._controller.CallToThread( do_it )
def _FetchPetition( self, content_type, status ):
( st, button ) = self._petition_types_to_controls[ ( content_type, status ) ]
def do_it():
try:
response = self._service.Request( HC.GET, 'petition', { 'content_type' : content_type, 'status' : status } )
self._current_petition = response[ 'petition' ]
wx.CallAfter( self._DrawCurrentPetition )
finally:
wx.CallAfter( button.Enable )
wx.CallAfter( button.SetLabelText, 'fetch ' + HC.content_status_string_lookup[ status ] + ' ' + HC.content_type_string_lookup[ content_type ] + ' petition' )
if self._current_petition is not None:
self._current_petition = None
self._DrawCurrentPetition()
button.Disable()
button.SetLabelText( u'Fetching\u2026' )
self._controller.CallToThread( do_it )
def _ShowHashes( self, hashes ):
file_service_key = self._management_controller.GetKey( 'file_service' )
@ -2266,14 +2426,6 @@ class ManagementPanelPetitions( ManagementPanel ):
self._controller.pub( 'swap_media_panel', self._page_key, panel )
def _DrawNumPetitions( self ):
self._num_petitions_text.SetLabelText( HydrusData.ConvertIntToPrettyString( self._num_petitions ) + ' petitions' )
if self._num_petitions > 0: self._get_petition.Enable()
else: self._get_petition.Disable()
def EventContentDoubleClick( self, event ):
selection = self._contents.GetSelection()
@ -2291,6 +2443,81 @@ class ManagementPanelPetitions( ManagementPanel ):
def EventProcess( self, event ):
def do_it( approved_contents, denied_contents, petition ):
try:
num_done = 0
num_to_do = len( approved_contents ) + len( denied_contents )
if num_to_do > 1:
job_key = ClientThreading.JobKey( cancellable = True )
job_key.SetVariable( 'popup_title', 'comitting petitions' )
HydrusGlobals.client_controller.pub( 'message', job_key )
else:
job_key = None
for content in approved_contents:
if job_key is not None:
( i_paused, should_quit ) = job_key.WaitIfNeeded()
if should_quit:
return
job_key.SetVariable( 'popup_gauge_1', ( num_done, num_to_do ) )
( update, content_update ) = petition.GetApproval( content )
self._service.Request( HC.POST, 'update', { 'client_to_server_update' : update } )
self._controller.Write( 'content_updates', { self._petition_service_key : [ content_update ] } )
num_done += 1
for content in denied_contents:
if job_key is not None:
( i_paused, should_quit ) = job_key.WaitIfNeeded()
if should_quit:
return
job_key.SetVariable( 'popup_gauge_1', ( num_done, num_to_do ) )
update = petition.GetDenial( content )
self._service.Request( HC.POST, 'update', { 'client_to_server_update' : update } )
num_done += 1
finally:
if job_key is not None:
job_key.Delete()
wx.CallAfter( self._FetchNumPetitions )
approved_contents = []
denied_contents = []
@ -2308,95 +2535,23 @@ class ManagementPanelPetitions( ManagementPanel ):
if len( approved_contents ) > 0:
for chunk_of_approved_contents in HydrusData.SplitListIntoChunks( approved_contents, 10 ):
update = self._current_petition.GetApproval( chunk_of_approved_contents )
self._service.Request( HC.POST, 'content_update_package', { 'update' : update } )
self._controller.Write( 'content_updates', { self._petition_service_key : update.GetContentUpdates( for_client = True ) } )
if len( denied_contents ) > 0:
for chunk_of_denied_contents in HydrusData.SplitListIntoChunks( denied_contents, 10 ):
update = self._current_petition.GetDenial( chunk_of_denied_contents )
self._service.Request( HC.POST, 'content_update_package', { 'update' : update } )
self._controller.Write( 'content_updates', { self._petition_service_key : update.GetContentUpdates( for_client = True ) } )
HydrusGlobals.client_controller.CallToThread( do_it, approved_contents, denied_contents, self._current_petition )
self._current_petition = None
self._DrawCurrentPetition()
self.EventRefreshNumPetitions( event )
def EventGetPetition( self, event ):
return
def do_it():
self._current_petition = self._service.Request( HC.GET, 'petition' )
wx.CallAfter( self._DrawCurrentPetition )
self._current_petition = None
self._DrawCurrentPetition()
self._controller.CallToThread( do_it )
def EventModifyPetitioner( self, event ):
with ClientGUIDialogs.DialogModifyAccounts( self, self._petition_service_key, ( self._current_petition.GetPetitionerIdentifier(), ) ) as dlg:
wx.MessageBox( 'modify users does not work yet!' )
with ClientGUIDialogs.DialogModifyAccounts( self, self._petition_service_key, ( self._current_petition.GetPetitionerAccount(), ) ) as dlg:
dlg.ShowModal()
def EventRefreshNumPetitions( self, event ):
return
def do_it():
try:
response = self._service.Request( HC.GET, 'num_petitions' )
self._num_petitions = response[ 'num_petitions' ]
wx.CallAfter( self._DrawNumPetitions )
if self._num_petitions > 0 and self._current_petition is None:
wx.CallAfter( self.EventGetPetition, None )
except Exception as e:
wx.CallAfter( self._num_petitions_text.SetLabel, 'Error' )
raise
self._num_petitions_text.SetLabelText( u'Fetching\u2026' )
self._controller.CallToThread( do_it )
def RefreshQuery( self, page_key ):
if page_key == self._page_key: self._DrawCurrentPetition()

View File

@ -419,28 +419,38 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
if do_it:
def process_in_thread( service_key, content_updates ):
for content_update in content_updates:
HydrusGlobals.client_controller.WriteSynchronous( 'content_updates', { service_key : [ content_update ] } )
local_file_services = ( CC.LOCAL_FILE_SERVICE_KEY, CC.TRASH_SERVICE_KEY )
if file_service_key in local_file_services:
# we want currently animating files (i.e. currently open files) to be unloaded before the delete call goes through
if file_service_key == CC.TRASH_SERVICE_KEY:
self._SetFocussedMedia( None )
# we want currently animating files (i.e. currently open files) to be unloaded before the delete call goes through
# split them into bits so we don't hang the gui with a huge delete transaction
wx.CallAfter( HydrusGlobals.client_controller.Write, 'content_updates', { file_service_key : [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, hashes ) ] } )
chunks_of_hashes = HydrusData.SplitListIntoChunks( hashes, 64 )
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, chunk_of_hashes ) for chunk_of_hashes in chunks_of_hashes ]
else:
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_PETITION, ( hashes, 'admin' ) )
service_keys_to_content_updates = { file_service_key : ( content_update, ) }
HydrusGlobals.client_controller.Write( 'content_updates', service_keys_to_content_updates )
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_PETITION, ( hashes, 'admin' ) ) ]
HydrusGlobals.client_controller.CallToThread( process_in_thread, file_service_key, content_updates )
@ -891,17 +901,6 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
def _NewThreadDumper( self ):
# can't do normal _getselectedhashes because we want to keep order!
args = [ media.GetHashes( CC.DISCRIMINANT_LOCAL ) for media in self._selected_media ]
hashes = [ h for h in itertools.chain( *args ) ]
if len( hashes ) > 0: HydrusGlobals.client_controller.pub( 'new_thread_dumper', hashes )
def _OpenExternally( self ):
if self._focussed_media is not None:
@ -2165,7 +2164,6 @@ class MediaPanelThumbnails( MediaPanel ):
elif command == 'manage_ratings': self._ManageRatings()
elif command == 'manage_tags': self._ManageTags()
elif command == 'modify_account': self._ModifyUploaders( data )
elif command == 'new_thread_dumper': self._NewThreadDumper()
elif command == 'open_externally': self._OpenExternally()
elif command == 'petition': self._PetitionFiles( data )
elif command == 'remove': self._Remove()
@ -2342,7 +2340,7 @@ class MediaPanelThumbnails( MediaPanel ):
if len( self._sorted_media ) > 0:
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
select_menu = wx.Menu()
@ -2678,7 +2676,7 @@ class MediaPanelThumbnails( MediaPanel ):
AddServiceKeyLabelsToMenu( menu, common_petitioned_ipfs_service_keys, unpin_phrase )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
#
@ -2817,7 +2815,7 @@ class MediaPanelThumbnails( MediaPanel ):
ClientGUIMenus.AppendMenuItem( self, custom_shortcuts_menu, 'manage', 'Manage your different custom filters and their shortcuts.', self._CustomFilter )
custom_shortcuts_menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( custom_shortcuts_menu )
for shortcut_name in shortcut_names:
@ -2834,7 +2832,7 @@ class MediaPanelThumbnails( MediaPanel ):
ClientGUIMenus.AppendMenu( menu, filter_menu, 'filter' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
if selection_has_inbox:
@ -2861,7 +2859,7 @@ class MediaPanelThumbnails( MediaPanel ):
# share
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
if selection_has_local:
@ -2967,14 +2965,14 @@ class MediaPanelThumbnails( MediaPanel ):
#
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'refresh', 'Refresh the current search.', HydrusGlobals.client_controller.pub, 'refresh_query', self._page_key )
ClientGUIMenus.AppendSeparator( menu )
if len( self._sorted_media ) > 0:
menu.AppendSeparator()
select_menu = wx.Menu()
if len( self._selected_media ) < len( self._sorted_media ):
@ -3017,13 +3015,13 @@ class MediaPanelThumbnails( MediaPanel ):
ClientGUIMenus.AppendMenu( menu, select_menu, 'select' )
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'open selection in a new page', 'Copy your current selection into a simple new page.', self._ShowSelectionInNewQueryPage )
if self._focussed_media.HasImages():
menu.AppendSeparator()
ClientGUIMenus.AppendSeparator( menu )
similar_menu = wx.Menu()

View File

@ -63,6 +63,20 @@ def AppendMenuLabel( menu, label, description = None ):
return menu_item
def AppendSeparator( menu ):
num_items = menu.GetMenuItemCount()
if num_items > 0:
last_item = menu.FindItemByPosition( num_items - 1 )
if not last_item.IsSeparator():
menu.AppendSeparator()
def BindMenuItem( event_handler, menu, menu_item, callable, *args, **kwargs ):
event_callable = GetEventCallable( callable, *args, **kwargs )

View File

@ -737,7 +737,22 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
self._options_panel = ClientGUICommon.StaticBox( self, 'options' )
self._get_tags_if_redundant = wx.CheckBox( self._options_panel )
menu_items = []
invert_call = self._InvertGetTagsIfURLKnownAndFileRedundant
value_call = self._GetTagsIfURLKnownAndFileRedundant
check_manager = ClientGUICommon.CheckboxManagerCalls( invert_call, value_call )
menu_items.append( ( 'check', 'get tags even if url is known and file is already in db (this downloader)', 'If this is selected, the client will fetch the tags from a file\'s page even if it has the file and already previously downloaded it from that location.', check_manager ) )
menu_items.append( ( 'separator', 0, 0, 0 ) )
check_manager = ClientGUICommon.CheckboxManagerOptions( 'get_tags_if_url_known_and_file_redundant' )
menu_items.append( ( 'check', 'get tags even if url is known and file is already in db (default)', 'Set the default for this value.', check_manager ) )
cog_button = ClientGUICommon.MenuBitmapButton( self._options_panel, CC.GlobalBMPs.cog, menu_items )
self._initial_file_limit = ClientGUICommon.NoneableSpinCtrl( self._options_panel, '', none_phrase = 'get everything', min = 1, max = 1000000 )
self._initial_file_limit.SetToolTipString( 'If set, the first sync will add no more than this many files. Otherwise, it will get everything the gallery has.' )
@ -769,7 +784,7 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
#
( name, gallery_identifier, gallery_stream_identifiers, query, period, get_tags_if_redundant, initial_file_limit, periodic_file_limit, paused, import_file_options, import_tag_options, self._last_checked, self._last_error, self._check_now, self._seed_cache ) = subscription.ToTuple()
( name, gallery_identifier, gallery_stream_identifiers, query, period, self._get_tags_if_url_known_and_file_redundant, initial_file_limit, periodic_file_limit, paused, import_file_options, import_tag_options, self._last_checked, self._last_error, self._check_now, self._seed_cache ) = subscription.ToTuple()
self._name.SetValue( name )
@ -797,7 +812,6 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
self._period.SetValue( period )
self._get_tags_if_redundant.SetValue( get_tags_if_redundant )
self._initial_file_limit.SetValue( initial_file_limit )
self._periodic_file_limit.SetValue( periodic_file_limit )
@ -844,12 +858,12 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
rows = []
rows.append( ( 'get tags even if new file is already in db: ', self._get_tags_if_redundant ) )
rows.append( ( 'on first check, get at most this many files: ', self._initial_file_limit ) )
rows.append( ( 'on normal checks, get at most this many newer files: ', self._periodic_file_limit ) )
gridbox = ClientGUICommon.WrapInGrid( self._options_panel, rows )
self._options_panel.AddF( cog_button, CC.FLAGS_LONE_BUTTON )
self._options_panel.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
#
@ -921,6 +935,16 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
return gallery_identifier
def _GetTagsIfURLKnownAndFileRedundant( self ):
return self._get_tags_if_url_known_and_file_redundant
def _InvertGetTagsIfURLKnownAndFileRedundant( self ):
self._get_tags_if_url_known_and_file_redundant = not self._get_tags_if_url_known_and_file_redundant
def _UpdateCommandButtons( self ):
on_initial_sync = self._last_checked == 0
@ -1090,7 +1114,6 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
period = self._period.GetValue()
get_tags_if_redundant = self._get_tags_if_redundant.GetValue()
initial_file_limit = self._initial_file_limit.GetValue()
periodic_file_limit = self._periodic_file_limit.GetValue()
@ -1100,7 +1123,7 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
import_tag_options = self._import_tag_options.GetOptions()
subscription.SetTuple( gallery_identifier, gallery_stream_identifiers, query, period, get_tags_if_redundant, initial_file_limit, periodic_file_limit, paused, import_file_options, import_tag_options, self._last_checked, self._last_error, self._check_now, self._seed_cache )
subscription.SetTuple( gallery_identifier, gallery_stream_identifiers, query, period, self._get_tags_if_url_known_and_file_redundant, initial_file_limit, periodic_file_limit, paused, import_file_options, import_tag_options, self._last_checked, self._last_error, self._check_now, self._seed_cache )
return subscription

View File

@ -3875,7 +3875,7 @@ class ManageSubscriptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
def _ConvertSubscriptionToTuples( self, subscription ):
( name, gallery_identifier, gallery_stream_identifiers, query, period, get_tags_if_redundant, initial_file_limit, periodic_file_limit, paused, import_file_options, import_tag_options, last_checked, last_error, check_now, seed_cache ) = subscription.ToTuple()
( name, gallery_identifier, gallery_stream_identifiers, query, period, get_tags_if_url_known_and_file_redundant, initial_file_limit, periodic_file_limit, paused, import_file_options, import_tag_options, last_checked, last_error, check_now, seed_cache ) = subscription.ToTuple()
pretty_site = gallery_identifier.ToString()

View File

@ -54,15 +54,16 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
self._pending_queries = []
self._get_tags_if_redundant = False
new_options = HydrusGlobals.client_controller.GetNewOptions()
self._get_tags_if_url_known_and_file_redundant = new_options.GetBoolean( 'get_tags_if_url_known_and_file_redundant' )
self._file_limit = HC.options[ 'gallery_file_limit' ]
self._gallery_paused = False
self._files_paused = False
self._import_file_options = ClientDefaults.GetDefaultImportFileOptions()
new_options = HydrusGlobals.client_controller.GetNewOptions()
self._import_tag_options = new_options.GetDefaultImportTagOptions( self._gallery_identifier )
self._seed_cache = SeedCache()
@ -100,12 +101,12 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
serialisable_current_query_stuff = ( self._current_query, self._current_query_num_urls, serialisable_current_gallery_stream_identifier, self._current_gallery_stream_identifier_page_index, serialisable_current_gallery_stream_identifier_found_urls, serialisable_pending_gallery_stream_identifiers )
return ( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, serialisable_current_query_stuff, self._pending_queries, self._get_tags_if_redundant, self._file_limit, self._gallery_paused, self._files_paused, serialisable_file_options, serialisable_tag_options, serialisable_seed_cache )
return ( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, serialisable_current_query_stuff, self._pending_queries, self._get_tags_if_url_known_and_file_redundant, self._file_limit, self._gallery_paused, self._files_paused, serialisable_file_options, serialisable_tag_options, serialisable_seed_cache )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, serialisable_current_query_stuff, self._pending_queries, self._get_tags_if_redundant, self._file_limit, self._gallery_paused, self._files_paused, serialisable_file_options, serialisable_tag_options, serialisable_seed_cache ) = serialisable_info
( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, serialisable_current_query_stuff, self._pending_queries, self._get_tags_if_url_known_and_file_redundant, self._file_limit, self._gallery_paused, self._files_paused, serialisable_file_options, serialisable_tag_options, serialisable_seed_cache ) = serialisable_info
( self._current_query, self._current_query_num_urls, serialisable_current_gallery_stream_identifier, self._current_gallery_stream_identifier_page_index, serialisable_current_gallery_stream_identifier_found_urls, serialisable_pending_gallery_stream_identifier ) = serialisable_current_query_stuff
@ -182,13 +183,13 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
tags = []
downloaded_tags = []
if status == CC.STATUS_REDUNDANT:
if self._get_tags_if_redundant and self._import_tag_options.ShouldFetchTags():
if self._get_tags_if_url_known_and_file_redundant and self._import_tag_options.InterestedInTags():
tags = gallery.GetTags( url, report_hooks = [ self._file_download_hook ] )
downloaded_tags = gallery.GetTags( url, report_hooks = [ self._file_download_hook ] )
else:
@ -203,9 +204,9 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
# status: x_out_of_y + 'downloading file'
if self._import_tag_options.ShouldFetchTags():
if self._import_tag_options.InterestedInTags():
tags = gallery.GetFileAndTags( temp_path, url, report_hooks = [ self._file_download_hook ] )
downloaded_tags = gallery.GetFileAndTags( temp_path, url, report_hooks = [ self._file_download_hook ] )
else:
@ -230,7 +231,7 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
if status in ( CC.STATUS_SUCCESSFUL, CC.STATUS_REDUNDANT ):
service_keys_to_content_updates = self._import_tag_options.GetServiceKeysToContentUpdates( hash, tags )
service_keys_to_content_updates = self._import_tag_options.GetServiceKeysToContentUpdates( hash, downloaded_tags )
if len( service_keys_to_content_updates ) > 0:
@ -503,7 +504,7 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
with self._lock:
return ( self._import_file_options, self._import_tag_options, self._get_tags_if_redundant, self._file_limit )
return ( self._import_file_options, self._import_tag_options, self._file_limit )
@ -522,6 +523,22 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
def GetTagsIfURLKnownAndFileRedundant( self ):
with self._lock:
return self._get_tags_if_url_known_and_file_redundant
def InvertGetTagsIfURLKnownAndFileRedundant( self ):
with self._lock:
self._get_tags_if_url_known_and_file_redundant = not self._get_tags_if_url_known_and_file_redundant
def PausePlayFiles( self ):
with self._lock:
@ -565,14 +582,6 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
def SetGetTagsIfRedundant( self, get_tags_if_redundant ):
with self._lock:
self._get_tags_if_redundant = get_tags_if_redundant
def SetImportFileOptions( self, import_file_options ):
with self._lock:
@ -1126,7 +1135,9 @@ class ImportFolder( HydrusSerialisable.SerialisableBaseNamed ):
if status in ( CC.STATUS_SUCCESSFUL, CC.STATUS_REDUNDANT ):
service_keys_to_content_updates = self._import_tag_options.GetServiceKeysToContentUpdates( hash, set() )
downloaded_tags = []
service_keys_to_content_updates = self._import_tag_options.GetServiceKeysToContentUpdates( hash, downloaded_tags ) # explicit tags
if len( service_keys_to_content_updates ) > 0:
@ -2056,9 +2067,11 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
( namespaces, search_value ) = ClientDefaults.GetDefaultNamespacesAndSearchValue( self._gallery_identifier )
new_options = HydrusGlobals.client_controller.GetNewOptions()
self._query = search_value
self._period = 86400 * 7
self._get_tags_if_redundant = False
self._get_tags_if_url_known_and_file_redundant = new_options.GetBoolean( 'get_tags_if_url_known_and_file_redundant' )
if HC.options[ 'gallery_file_limit' ] is None:
@ -2102,12 +2115,12 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
serialisable_tag_options = self._import_tag_options.GetSerialisableTuple()
serialisable_seed_cache = self._seed_cache.GetSerialisableTuple()
return ( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, self._query, self._period, self._get_tags_if_redundant, self._initial_file_limit, self._periodic_file_limit, self._paused, serialisable_file_options, serialisable_tag_options, self._last_checked, self._check_now, self._last_error, serialisable_seed_cache )
return ( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, self._query, self._period, self._get_tags_if_url_known_and_file_redundant, self._initial_file_limit, self._periodic_file_limit, self._paused, serialisable_file_options, serialisable_tag_options, self._last_checked, self._check_now, self._last_error, serialisable_seed_cache )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, self._query, self._period, self._get_tags_if_redundant, self._initial_file_limit, self._periodic_file_limit, self._paused, serialisable_file_options, serialisable_tag_options, self._last_checked, self._check_now, self._last_error, serialisable_seed_cache ) = serialisable_info
( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, self._query, self._period, self._get_tags_if_url_known_and_file_redundant, self._initial_file_limit, self._periodic_file_limit, self._paused, serialisable_file_options, serialisable_tag_options, self._last_checked, self._check_now, self._last_error, serialisable_seed_cache ) = serialisable_info
self._gallery_identifier = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_gallery_identifier )
self._gallery_stream_identifiers = [ HydrusSerialisable.CreateFromSerialisableTuple( serialisable_gallery_stream_identifier ) for serialisable_gallery_stream_identifier in serialisable_gallery_stream_identifiers ]
@ -2125,11 +2138,11 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
if version == 1:
( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, query, period, get_tags_if_redundant, initial_file_limit, periodic_file_limit, paused, serialisable_file_options, serialisable_tag_options, last_checked, last_error, serialisable_seed_cache ) = old_serialisable_info
( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, query, period, get_tags_if_url_known_and_file_redundant, initial_file_limit, periodic_file_limit, paused, serialisable_file_options, serialisable_tag_options, last_checked, last_error, serialisable_seed_cache ) = old_serialisable_info
check_now = False
new_serialisable_info = ( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, query, period, get_tags_if_redundant, initial_file_limit, periodic_file_limit, paused, serialisable_file_options, serialisable_tag_options, last_checked, check_now, last_error, serialisable_seed_cache )
new_serialisable_info = ( serialisable_gallery_identifier, serialisable_gallery_stream_identifiers, query, period, get_tags_if_url_known_and_file_redundant, initial_file_limit, periodic_file_limit, paused, serialisable_file_options, serialisable_tag_options, last_checked, check_now, last_error, serialisable_seed_cache )
return ( 2, new_serialisable_info )
@ -2190,15 +2203,15 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
tags = []
downloaded_tags = []
if status == CC.STATUS_REDUNDANT:
if self._get_tags_if_redundant and self._import_tag_options.ShouldFetchTags():
if self._get_tags_if_url_known_and_file_redundant and self._import_tag_options.InterestedInTags():
job_key.SetVariable( 'popup_text_1', x_out_of_y + 'found file in db, fetching tags' )
tags = gallery.GetTags( url, report_hooks = [ hook ] )
downloaded_tags = gallery.GetTags( url, report_hooks = [ hook ] )
else:
@ -2213,9 +2226,9 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
job_key.SetVariable( 'popup_text_1', x_out_of_y + 'downloading file' )
if self._import_tag_options.ShouldFetchTags():
if self._import_tag_options.InterestedInTags():
tags = gallery.GetFileAndTags( temp_path, url, report_hooks = [ hook ] )
downloaded_tags = gallery.GetFileAndTags( temp_path, url, report_hooks = [ hook ] )
else:
@ -2257,7 +2270,7 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
if hash is not None:
service_keys_to_content_updates = self._import_tag_options.GetServiceKeysToContentUpdates( hash, tags )
service_keys_to_content_updates = self._import_tag_options.GetServiceKeysToContentUpdates( hash, downloaded_tags )
if len( service_keys_to_content_updates ) > 0:
@ -2494,13 +2507,13 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
self._seed_cache = SeedCache()
def SetTuple( self, gallery_identifier, gallery_stream_identifiers, query, period, get_tags_if_redundant, initial_file_limit, periodic_file_limit, paused, import_file_options, import_tag_options, last_checked, last_error, check_now, seed_cache ):
def SetTuple( self, gallery_identifier, gallery_stream_identifiers, query, period, get_tags_if_url_known_and_file_redundant, initial_file_limit, periodic_file_limit, paused, import_file_options, import_tag_options, last_checked, last_error, check_now, seed_cache ):
self._gallery_identifier = gallery_identifier
self._gallery_stream_identifiers = gallery_stream_identifiers
self._query = query
self._period = period
self._get_tags_if_redundant = get_tags_if_redundant
self._get_tags_if_url_known_and_file_redundant = get_tags_if_url_known_and_file_redundant
self._initial_file_limit = initial_file_limit
self._periodic_file_limit = periodic_file_limit
self._paused = paused
@ -2575,7 +2588,7 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
def ToTuple( self ):
return ( self._name, self._gallery_identifier, self._gallery_stream_identifiers, self._query, self._period, self._get_tags_if_redundant, self._initial_file_limit, self._periodic_file_limit, self._paused, self._import_file_options, self._import_tag_options, self._last_checked, self._last_error, self._check_now, self._seed_cache )
return ( self._name, self._gallery_identifier, self._gallery_stream_identifiers, self._query, self._period, self._get_tags_if_url_known_and_file_redundant, self._initial_file_limit, self._periodic_file_limit, self._paused, self._import_file_options, self._import_tag_options, self._last_checked, self._last_error, self._check_now, self._seed_cache )
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_SUBSCRIPTION ] = Subscription
@ -2671,7 +2684,7 @@ class ThreadWatcherImport( HydrusSerialisable.SerialisableBase ):
file_original_filename = self._urls_to_filenames[ file_url ]
tags = [ 'filename:' + file_original_filename ]
downloaded_tags = [ 'filename:' + file_original_filename ]
# we now do both url and md5 tests here because cloudflare was sometimes giving optimised versions of images, meaning the api's md5 was unreliable
# if someone set up a thread watcher of a thread they had previously watched, any optimised images would be redownloaded
@ -2736,7 +2749,7 @@ class ThreadWatcherImport( HydrusSerialisable.SerialisableBase ):
with self._lock:
service_keys_to_content_updates = self._import_tag_options.GetServiceKeysToContentUpdates( hash, tags )
service_keys_to_content_updates = self._import_tag_options.GetServiceKeysToContentUpdates( hash, downloaded_tags )
if len( service_keys_to_content_updates ) > 0:

View File

@ -9,6 +9,7 @@ import HydrusGlobals
import HydrusThreading
import HydrusVideoHandling
import lz4
import os
import threading
import time
import wx
@ -196,6 +197,17 @@ class RasterContainerVideo( RasterContainer ):
duration = self._media.GetDuration()
num_frames = self._media.GetNumFrames()
if num_frames is None or num_frames == 0:
message = 'The file with hash ' + media.GetHash().encode( 'hex' ) + ', had an invalid number of frames.'
message += os.linesep * 2
message += 'You may wish to try exporting, deleting, and the reimporting it.'
HydrusData.ShowText( message )
num_frames = 1
self._average_frame_duration = float( duration ) / num_frames
frame_buffer_length = ( video_buffer_size_mb * 1024 * 1024 ) / ( x * y * 3 )
@ -622,4 +634,4 @@ class HydrusBitmap( object ):
def GetSize( self ): return self._size

View File

@ -49,7 +49,7 @@ options = {}
# Misc
NETWORK_VERSION = 18
SOFTWARE_VERSION = 246
SOFTWARE_VERSION = 247
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
@ -75,6 +75,13 @@ CONTENT_STATUS_PENDING = 1
CONTENT_STATUS_DELETED = 2
CONTENT_STATUS_PETITIONED = 3
content_status_string_lookup = {}
content_status_string_lookup[ CONTENT_STATUS_CURRENT ] = 'current'
content_status_string_lookup[ CONTENT_STATUS_PENDING ] = 'pending'
content_status_string_lookup[ CONTENT_STATUS_DELETED ] = 'deleted'
content_status_string_lookup[ CONTENT_STATUS_PETITIONED ] = 'petitioned'
CONTENT_TYPE_MAPPINGS = 0
CONTENT_TYPE_TAG_SIBLINGS = 1
CONTENT_TYPE_TAG_PARENTS = 2
@ -277,21 +284,21 @@ service_string_lookup[ IPFS ] = 'ipfs daemon'
service_string_lookup[ SERVER_ADMIN ] = 'hydrus server administration service'
service_string_lookup[ NULL_SERVICE ] = 'null service'
LOCAL_FILE_SERVICES = [ LOCAL_FILE_DOMAIN, LOCAL_FILE_TRASH_DOMAIN, COMBINED_LOCAL_FILE ]
LOCAL_TAG_SERVICES = [ LOCAL_TAG ]
LOCAL_FILE_SERVICES = ( LOCAL_FILE_DOMAIN, LOCAL_FILE_TRASH_DOMAIN, COMBINED_LOCAL_FILE )
LOCAL_TAG_SERVICES = ( LOCAL_TAG, )
LOCAL_SERVICES = list( LOCAL_FILE_SERVICES ) + list( LOCAL_TAG_SERVICES ) + [ LOCAL_RATING_LIKE, LOCAL_RATING_NUMERICAL, LOCAL_BOORU ]
LOCAL_SERVICES = LOCAL_FILE_SERVICES + LOCAL_TAG_SERVICES + ( LOCAL_RATING_LIKE, LOCAL_RATING_NUMERICAL, LOCAL_BOORU )
RATINGS_SERVICES = [ LOCAL_RATING_LIKE, LOCAL_RATING_NUMERICAL, RATING_LIKE_REPOSITORY, RATING_NUMERICAL_REPOSITORY ]
REPOSITORIES = [ TAG_REPOSITORY, FILE_REPOSITORY, RATING_LIKE_REPOSITORY, RATING_NUMERICAL_REPOSITORY ]
RESTRICTED_SERVICES = list( REPOSITORIES ) + [ SERVER_ADMIN, MESSAGE_DEPOT ]
REMOTE_SERVICES = list( RESTRICTED_SERVICES ) + [ IPFS ]
FILE_SERVICES = list( LOCAL_FILE_SERVICES ) + [ FILE_REPOSITORY, IPFS ]
TAG_SERVICES = [ LOCAL_TAG, TAG_REPOSITORY ]
ADDREMOVABLE_SERVICES = [ LOCAL_RATING_LIKE, LOCAL_RATING_NUMERICAL, FILE_REPOSITORY, TAG_REPOSITORY, SERVER_ADMIN, IPFS ]
NONEDITABLE_SERVICES = [ LOCAL_FILE_DOMAIN, LOCAL_FILE_TRASH_DOMAIN, LOCAL_TAG, COMBINED_FILE, COMBINED_TAG, COMBINED_LOCAL_FILE ]
AUTOCOMPLETE_CACHE_SPECIFIC_FILE_SERVICES = [ LOCAL_FILE_DOMAIN, LOCAL_FILE_TRASH_DOMAIN, COMBINED_LOCAL_FILE, FILE_REPOSITORY ]
ALL_SERVICES = list( REMOTE_SERVICES ) + list( LOCAL_SERVICES ) + [ COMBINED_FILE, COMBINED_TAG ]
RATINGS_SERVICES = ( LOCAL_RATING_LIKE, LOCAL_RATING_NUMERICAL, RATING_LIKE_REPOSITORY, RATING_NUMERICAL_REPOSITORY )
REPOSITORIES = ( TAG_REPOSITORY, FILE_REPOSITORY, RATING_LIKE_REPOSITORY, RATING_NUMERICAL_REPOSITORY )
RESTRICTED_SERVICES = REPOSITORIES + ( SERVER_ADMIN, MESSAGE_DEPOT )
REMOTE_SERVICES = RESTRICTED_SERVICES + ( IPFS, )
FILE_SERVICES = LOCAL_FILE_SERVICES + ( FILE_REPOSITORY, IPFS )
TAG_SERVICES = ( LOCAL_TAG, TAG_REPOSITORY )
ADDREMOVABLE_SERVICES = ( LOCAL_RATING_LIKE, LOCAL_RATING_NUMERICAL, FILE_REPOSITORY, TAG_REPOSITORY, SERVER_ADMIN, IPFS )
NONEDITABLE_SERVICES = ( LOCAL_FILE_DOMAIN, LOCAL_FILE_TRASH_DOMAIN, LOCAL_TAG, COMBINED_FILE, COMBINED_TAG, COMBINED_LOCAL_FILE )
AUTOCOMPLETE_CACHE_SPECIFIC_FILE_SERVICES = ( LOCAL_FILE_DOMAIN, LOCAL_FILE_TRASH_DOMAIN, COMBINED_LOCAL_FILE, FILE_REPOSITORY )
ALL_SERVICES = REMOTE_SERVICES + LOCAL_SERVICES + ( COMBINED_FILE, COMBINED_TAG )
SERVICES_WITH_THUMBNAILS = [ FILE_REPOSITORY, LOCAL_FILE_DOMAIN ]

View File

@ -311,11 +311,11 @@ class HydrusDB( object ):
if unique:
create_phrase = 'CREATE UNIQUE INDEX '
create_phrase = 'CREATE UNIQUE INDEX IF NOT EXISTS '
else:
create_phrase = 'CREATE INDEX '
create_phrase = 'CREATE INDEX IF NOT EXISTS '
on_phrase = ' ON ' + table_name_simple + ' (' + ', '.join( columns ) + ');'
@ -567,6 +567,13 @@ class HydrusDB( object ):
return [ row for row in self._SelectFromList( select_statement, xs ) ]
def _STI( self, iterable_cursor ):
# strip singleton tuples to an iterator
return ( item for ( item, ) in iterable_cursor )
def _STL( self, iterable_cursor ):
# strip singleton tuples to a list

View File

@ -7,93 +7,9 @@ import HydrusNetworking
import HydrusSerialisable
import threading
INT_PARAMS = { 'expires', 'num', 'since', 'content_type', 'action' }
INT_PARAMS = { 'expires', 'num', 'since', 'content_type', 'action', 'status' }
BYTE_PARAMS = { 'access_key', 'account_type_key', 'subject_account_key', 'hash', 'registration_key', 'subject_hash', 'subject_tag', 'share_key', 'update_hash' }
def ConvertContentsToClientToServerContentUpdatePackage( action, contents, reason = None ):
hashes_to_hash_ids = {}
hash_ids_to_hashes = {}
hash_i = 0
content_data_dict = HydrusData.GetEmptyDataDict()
for content in contents:
content_type = content.GetContentType()
content_data = content.GetContentData()
if content_type == HC.CONTENT_TYPE_FILES:
hashes = content_data
elif content_type == HC.CONTENT_TYPE_MAPPINGS:
( tag, hashes ) = content_data
elif content_type in ( HC.CONTENT_TYPE_TAG_PARENTS, HC.CONTENT_TYPE_TAG_SIBLINGS ):
( old_tag, new_tag ) = content_data
hashes = set()
hash_ids = []
for hash in hashes:
if hash not in hashes_to_hash_ids:
hashes_to_hash_ids[ hash ] = hash_i
hash_ids_to_hashes[ hash_i ] = hash
hash_i += 1
hash_ids.append( hashes_to_hash_ids[ hash ] )
if action in ( HC.CONTENT_UPDATE_PEND, HC.CONTENT_UPDATE_PETITION ):
if reason is None:
reason = 'admin'
if content_type == HC.CONTENT_TYPE_FILES:
row = ( hash_ids, reason )
elif content_type == HC.CONTENT_TYPE_MAPPINGS:
row = ( tag, hash_ids, reason )
else:
row = ( ( old_tag, new_tag ), reason )
elif action in ( HC.CONTENT_UPDATE_DENY_PEND, HC.CONTENT_UPDATE_DENY_PETITION ):
if content_type == HC.CONTENT_TYPE_FILES:
row = hash_ids
elif content_type == HC.CONTENT_TYPE_MAPPINGS:
row = ( tag, hash_ids )
elif content_type in ( HC.CONTENT_TYPE_TAG_PARENTS, HC.CONTENT_TYPE_TAG_SIBLINGS ):
row = ( old_tag, new_tag )
content_data_dict[ content_type ][ action ].append( row )
return ClientToServerContentUpdatePackage( content_data_dict, hash_ids_to_hashes )
def GenerateDefaultServiceDictionary( service_type ):
dictionary = HydrusSerialisable.SerialisableDictionary()
@ -1892,29 +1808,29 @@ class Petition( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PETITION
SERIALISABLE_VERSION = 1
def __init__( self, action = None, petitioner_account_identifier = None, reason = None, contents = None ):
def __init__( self, action = None, petitioner_account = None, reason = None, contents = None ):
HydrusSerialisable.SerialisableBase.__init__( self )
self._action = action
self._petitioner_account_identifier = petitioner_account_identifier
self._petitioner_account = petitioner_account
self._reason = reason
self._contents = contents
def _GetSerialisableInfo( self ):
serialisable_petitioner_account_identifier = self._petitioner_account_identifier.GetSerialisableTuple()
serialisable_petitioner_account = Account.GenerateSerialisableTupleFromAccount( self._petitioner_account )
serialisable_contents = [ content.GetSerialisableTuple() for content in self._contents ]
return ( self._action, serialisable_petitioner_account_identifier, self._reason, serialisable_contents )
return ( self._action, serialisable_petitioner_account, self._reason, serialisable_contents )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( self._action, serialisable_petitioner_account_identifier, self._reason, serialisable_contents ) = serialisable_info
( self._action, serialisable_petitioner_account, self._reason, serialisable_contents ) = serialisable_info
self._petitioner_account_identifier = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_petitioner_account_identifier )
self._petitioner_account = Account.GenerateAccountFromSerialisableTuple( serialisable_petitioner_account )
self._contents = [ HydrusSerialisable.CreateFromSerialisableTuple( serialisable_content ) for serialisable_content in serialisable_contents ]
@ -1925,20 +1841,39 @@ class Petition( HydrusSerialisable.SerialisableBase ):
if self._action == HC.CONTENT_UPDATE_PEND:
action_text += 'ADD'
action_colour = ( 0, 128, 0 )
action_colour = ( 127, 255, 127 )
elif self._action == HC.CONTENT_UPDATE_PETITION:
action_text += 'DELETE'
action_colour = ( 128, 0, 0 )
action_colour = ( 255, 127, 127 )
return ( action_text, action_colour )
def GetApproval( self, filtered_contents ):
def GetApproval( self, content ):
return ConvertContentsToClientToServerContentUpdatePackage( self._action, filtered_contents, reason = self._reason )
if self._action == HC.CONTENT_UPDATE_PEND:
content_update_action = HC.CONTENT_UPDATE_ADD
elif self._action == HC.CONTENT_UPDATE_PETITION:
content_update_action = HC.CONTENT_UPDATE_DELETE
update = ClientToServerUpdate()
update.AddContent( self._action, content, self._reason )
content_type = content.GetContentType()
row = content.GetContentData()
content_update = HydrusData.ContentUpdate( content_type, content_update_action, row )
return ( update, content_update )
def GetContents( self ):
@ -1946,7 +1881,7 @@ class Petition( HydrusSerialisable.SerialisableBase ):
return self._contents
def GetDenial( self, filtered_contents ):
def GetDenial( self, content ):
if self._action == HC.CONTENT_UPDATE_PEND:
@ -1957,12 +1892,16 @@ class Petition( HydrusSerialisable.SerialisableBase ):
denial_action = HC.CONTENT_UPDATE_DENY_PETITION
return ConvertContentsToClientToServerContentUpdatePackage( denial_action, filtered_contents )
update = ClientToServerUpdate()
update.AddContent( denial_action, content, self._reason )
return update
def GetPetitionerIdentifier( self ):
def GetPetitionerAccount( self ):
return self._petitioner_account_identifier
return self._petitioner_account
def GetReason( self ):

View File

@ -269,7 +269,14 @@ class SerialisableBytesDictionary( SerialisableBase, dict ):
for ( key, value ) in self.items():
encoded_key = key.encode( 'hex' )
if isinstance( key, int ):
encoded_key = key
else:
encoded_key = key.encode( 'hex' )
if isinstance( value, ( list, tuple, set ) ):
@ -290,7 +297,14 @@ class SerialisableBytesDictionary( SerialisableBase, dict ):
for ( encoded_key, encoded_value ) in serialisable_info:
key = encoded_key.decode( 'hex' )
if isinstance( encoded_key, int ):
key = encoded_key
else:
key = encoded_key.decode( 'hex' )
if isinstance( encoded_value, ( list, tuple, set ) ):

View File

@ -189,28 +189,31 @@ class HydrusTagArchive( object ):
def GetHashType( self ):
try: ( hash_type, ) = self._c.execute( 'SELECT hash_type FROM hash_type;' ).fetchone()
except:
result = self._c.execute( 'SELECT hash_type FROM hash_type;' ).fetchone()
if result is None:
try:
( hash, ) = self._c.execute( 'SELECT hash FROM hashes;' ).fetchone()
if len( hash ) == 16: self.SetHashType( HASH_TYPE_MD5 )
elif len( hash ) == 20: self.SetHashType( HASH_TYPE_SHA1 )
elif len( hash ) == 32: self.SetHashType( HASH_TYPE_SHA256 )
elif len( hash ) == 64: self.SetHashType( HASH_TYPE_SHA512 )
else: raise Exception()
return self.GetHashType()
except TypeError:
result = self._c.execute( 'SELECT hash FROM hashes;' ).fetchone()
if result is None:
raise Exception( 'This archive has no hash type set, and as it has no files, no hash type guess can be made.' )
return hash_type
if len( hash ) == 16: self.SetHashType( HASH_TYPE_MD5 )
elif len( hash ) == 20: self.SetHashType( HASH_TYPE_SHA1 )
elif len( hash ) == 32: self.SetHashType( HASH_TYPE_SHA256 )
elif len( hash ) == 64: self.SetHashType( HASH_TYPE_SHA512 )
else: raise Exception()
return self.GetHashType()
else:
( hash_type, ) = result
return hash_type
def GetMappings( self, hash ): return self.GetTags( hash )
@ -244,7 +247,17 @@ class HydrusTagArchive( object ):
return True
except: return False
except:
return False
def HasHashTypeSet( self ):
result = self._c.execute( 'SELECT hash_type FROM hash_type;' ).fetchone()
return result is not None
def IterateHashes( self ):

View File

@ -38,7 +38,7 @@ def CensorshipMatch( tag, censorships ):
( namespace, subtag ) = SplitTag( tag )
if namespace == censorship:
if namespace == censorship[:-1]:
return True
@ -238,13 +238,17 @@ def CombineTag( namespace, subtag ):
def RenderTag( tag ):
if tag.startswith( '::' ):
( namespace, subtag ) = SplitTag( tag )
if namespace == '':
return tag[1:]
return subtag
else:
return tag
connector = ':'
return namespace + connector + subtag
def SplitTag( tag ):

View File

@ -346,8 +346,6 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'BEGIN IMMEDIATE' )
now = HydrusData.GetNow()
self._c.execute( 'CREATE TABLE services ( service_id INTEGER PRIMARY KEY, service_key BLOB_BYTES, service_type INTEGER, name TEXT, port INTEGER, dictionary_string TEXT );' )
self._c.execute( 'CREATE TABLE accounts ( account_id INTEGER PRIMARY KEY, service_id INTEGER, account_key BLOB_BYTES, hashed_access_key BLOB_BYTES, account_type_id INTEGER, created INTEGER, expires INTEGER, dictionary_string TEXT );' )
@ -455,8 +453,6 @@ class DB( HydrusDB.HydrusDB ):
def _GenerateRegistrationKeys( self, service_id, num, account_type_id, expires, force_registration_key = None ):
now = HydrusData.GetNow()
if force_registration_key is None:
keys = [ ( os.urandom( HC.HYDRUS_KEY_LENGTH ), os.urandom( HC.HYDRUS_KEY_LENGTH ), os.urandom( HC.HYDRUS_KEY_LENGTH ) ) for i in range( num ) ]
@ -682,9 +678,12 @@ class DB( HydrusDB.HydrusDB ):
path = ServerFiles.GetFilePath( hash )
with open( path, 'rb' ) as f: file = f.read()
with open( path, 'rb' ) as f:
data = f.read()
return file
return data
def _GetHash( self, master_hash_id ):
@ -1099,8 +1098,6 @@ class DB( HydrusDB.HydrusDB ):
account_type_id = self._GetAccountTypeId( service_id, account_type_key )
account_ids_to_change = self._c.execute( 'SELECT account_id FROM accounts WHERE service_id = ? AND account_type_id = ?;', ( service_id, account_type_id ) )
if account_type_key not in deletee_account_type_keys_to_new_account_type_keys:
raise HydrusExceptions.NotFoundException( 'Was missing a replacement account_type_key.' )
@ -1224,8 +1221,6 @@ class DB( HydrusDB.HydrusDB ):
def _RepositoryAddFile( self, service_id, account_id, file_dict, overwrite_deleted ):
size = file_dict[ 'size' ]
master_hash_id = self._AddFile( file_dict )
service_hash_id = self._RepositoryGetServiceHashId( service_id, master_hash_id )
@ -1287,7 +1282,7 @@ class DB( HydrusDB.HydrusDB ):
select_statement = 'SELECT service_hash_id FROM ' + deleted_mappings_table_name + ' WHERE service_tag_id = ' + str( service_tag_id ) + ' AND service_hash_id IN %s;'
deleted_service_hash_ids = ( service_hash_id for ( service_hash_id, ) in self._SelectFromList( select_statement, service_hash_ids ) )
deleted_service_hash_ids = self._STI( self._SelectFromList( select_statement, service_hash_ids ) )
service_hash_ids = set( service_hash_ids ).difference( deleted_service_hash_ids )
@ -1537,7 +1532,7 @@ class DB( HydrusDB.HydrusDB ):
select_statement = 'SELECT service_hash_id FROM ' + current_files_table_name + ' WHERE service_hash_id IN %s;'
valid_service_hash_ids = [ service_hash_id for ( service_hash_id, ) in self._SelectFromList( select_statement, service_hash_ids ) ]
valid_service_hash_ids = self._STL( self._SelectFromList( select_statement, service_hash_ids ) )
self._RepositoryRewardFilePetitioners( service_id, valid_service_hash_ids, 1 )
@ -1553,9 +1548,9 @@ class DB( HydrusDB.HydrusDB ):
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateRepositoryMappingsTableNames( service_id )
select_statement = 'SELECT service_hash_id FROM ' + current_mappings_table_name + ' WHERE service_tag_id = ? AND service_hash_id IN %s;'
select_statement = 'SELECT service_hash_id FROM ' + current_mappings_table_name + ' WHERE service_tag_id = ' + str( service_tag_id ) + ' AND service_hash_id IN %s;'
valid_service_hash_ids = [ service_hash_id for ( service_hash_id, ) in self._SelectFromList( select_statement, service_hash_ids ) ]
valid_service_hash_ids = self._STL( self._SelectFromList( select_statement, service_hash_ids ) )
self._RepositoryRewardMappingPetitioners( service_id, service_tag_id, valid_service_hash_ids, 1 )
@ -1599,9 +1594,9 @@ class DB( HydrusDB.HydrusDB ):
self._RepositoryRewardFilePetitioners( service_id, service_hash_ids, -1 )
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateRepositoryMappingsTableNames( service_id )
( current_files_table_name, deleted_files_table_name, pending_files_table_name, petitioned_files_table_name, ip_addresses_table_name ) = GenerateRepositoryFilesTableNames( service_id )
self._c.executemany( 'DELETE FROM ' + petitioned_mappings_table_name + ' WHERE service_hash_id = ?;', ( ( service_hash_id, ) for service_hash_id in service_hash_ids ) )
self._c.executemany( 'DELETE FROM ' + petitioned_files_table_name + ' WHERE service_hash_id = ?;', ( ( service_hash_id, ) for service_hash_id in service_hash_ids ) )
def _RepositoryDenyMappingPetition( self, service_id, service_tag_id, service_hash_ids ):
@ -2100,46 +2095,6 @@ class DB( HydrusDB.HydrusDB ):
return petition_count_info
def _RepositoryGetPetition( self, service_key, account, content_type, status ):
service_id = self._GetServiceId( service_key )
account.CheckPermission( content_type, HC.PERMISSION_ACTION_OVERRULE )
if content_type == HC.CONTENT_TYPE_FILES:
petition = self._RepositoryGetFilePetition( service_id )
elif content_type == HC.CONTENT_TYPE_MAPPINGS:
petition = self._RepositoryGetMappingPetition( service_id )
elif content_type == HC.CONTENT_TYPE_TAG_PARENTS:
if status == HC.CONTENT_STATUS_PENDING:
petition = self._RepositoryGetTagParentPend( service_id )
else:
petition = self._RepositoryGetTagParentPetition( service_id )
elif content_type == HC.CONTENT_TYPE_TAG_SIBLINGS:
if status == HC.CONTENT_STATUS_PENDING:
petition = self._RepositoryGetTagSiblingPend( service_id )
else:
petition = self._RepositoryGetTagSiblingPetition( service_id )
return petition
def _RepositoryGetMappingPetition( self, service_id ):
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateRepositoryMappingsTableNames( service_id )
@ -2213,6 +2168,46 @@ class DB( HydrusDB.HydrusDB ):
return master_tag_id
def _RepositoryGetPetition( self, service_key, account, content_type, status ):
service_id = self._GetServiceId( service_key )
account.CheckPermission( content_type, HC.PERMISSION_ACTION_OVERRULE )
if content_type == HC.CONTENT_TYPE_FILES:
petition = self._RepositoryGetFilePetition( service_id )
elif content_type == HC.CONTENT_TYPE_MAPPINGS:
petition = self._RepositoryGetMappingPetition( service_id )
elif content_type == HC.CONTENT_TYPE_TAG_PARENTS:
if status == HC.CONTENT_STATUS_PENDING:
petition = self._RepositoryGetTagParentPend( service_id )
else:
petition = self._RepositoryGetTagParentPetition( service_id )
elif content_type == HC.CONTENT_TYPE_TAG_SIBLINGS:
if status == HC.CONTENT_STATUS_PENDING:
petition = self._RepositoryGetTagSiblingPend( service_id )
else:
petition = self._RepositoryGetTagSiblingPetition( service_id )
return petition
def _RepositoryGetServiceHashId( self, service_id, master_hash_id ):
( hash_id_map_table_name, tag_id_map_table_name ) = GenerateRepositoryMasterMapTableNames( service_id )
@ -2305,7 +2300,7 @@ class DB( HydrusDB.HydrusDB ):
( current_tag_parents_table_name, deleted_tag_parents_table_name, pending_tag_parents_table_name, petitioned_tag_parents_table_name ) = GenerateRepositoryTagParentsTableNames( service_id )
result = self._c.execute( 'SELECT account_id, reason_id FROM ' + pending_tag_parents_table_name + ' WHERE ORDER BY RANDOM() LIMIT 1;' ).fetchone()
result = self._c.execute( 'SELECT account_id, reason_id FROM ' + pending_tag_parents_table_name + ' ORDER BY RANDOM() LIMIT 1;' ).fetchone()
if result is None:
@ -2341,7 +2336,7 @@ class DB( HydrusDB.HydrusDB ):
( current_tag_parents_table_name, deleted_tag_parents_table_name, pending_tag_parents_table_name, petitioned_tag_parents_table_name ) = GenerateRepositoryTagParentsTableNames( service_id )
result = self._c.execute( 'SELECT account_id, reason_id FROM ' + petitioned_tag_parents_table_name + ' WHERE ORDER BY RANDOM() LIMIT 1;' ).fetchone()
result = self._c.execute( 'SELECT account_id, reason_id FROM ' + petitioned_tag_parents_table_name + ' ORDER BY RANDOM() LIMIT 1;' ).fetchone()
if result is None:
@ -2404,7 +2399,7 @@ class DB( HydrusDB.HydrusDB ):
bad_tag = self._GetTag( bad_master_tag_id )
good_tag = self._GetTag( good_master_tag_id )
content = HydrusNetwork.Content( HC.CONTENT_TYPE_TAG_PARENTS, ( bad_tag, good_tag ) )
content = HydrusNetwork.Content( HC.CONTENT_TYPE_TAG_SIBLINGS, ( bad_tag, good_tag ) )
contents.append( content )
@ -2443,7 +2438,7 @@ class DB( HydrusDB.HydrusDB ):
bad_tag = self._GetTag( bad_master_tag_id )
good_tag = self._GetTag( good_master_tag_id )
content = HydrusNetwork.Content( HC.CONTENT_TYPE_TAG_PARENTS, ( bad_tag, good_tag ) )
content = HydrusNetwork.Content( HC.CONTENT_TYPE_TAG_SIBLINGS, ( bad_tag, good_tag ) )
contents.append( content )
@ -2532,7 +2527,7 @@ class DB( HydrusDB.HydrusDB ):
now = HydrusData.GetNow()
self._c.executemany( 'REPLACE INTO ' + petitioned_files_table_name + ' ( service_hash_id, account_id, reason_id ) VALUES ( ?, ?, ? );', ( ( service_hash_id, account_id, now ) for service_hash_id in valid_service_hash_ids ) )
self._c.executemany( 'REPLACE INTO ' + petitioned_files_table_name + ' ( service_hash_id, account_id, reason_id ) VALUES ( ?, ?, ? );', ( ( service_hash_id, account_id, reason_id ) for service_hash_id in valid_service_hash_ids ) )
def _RepositoryPetitionMappings( self, service_id, account_id, service_tag_id, service_hash_ids, reason_id ):
@ -4000,7 +3995,10 @@ class DB( HydrusDB.HydrusDB ):
result = self._c.execute( 'SELECT 1 FROM registration_keys WHERE service_id = ? AND access_key = ?;', ( service_id, sqlite3.Binary( access_key ) ) ).fetchone()
if result is None: return False
if result is None:
return False
return True

View File

@ -56,8 +56,8 @@ class HydrusServiceRepository( HydrusServiceRestricted ):
root = HydrusServiceRestricted._InitRoot( self )
#root.putChild( 'num_petitions', ServerServerResources.HydrusResourceRestrictedNumPetitions( self._service, HydrusServer.REMOTE_DOMAIN ) )
#root.putChild( 'petition', ServerServerResources.HydrusResourceRestrictedPetition( self._service, HydrusServer.REMOTE_DOMAIN ) )
root.putChild( 'num_petitions', ServerServerResources.HydrusResourceRestrictedNumPetitions( self._service, HydrusServer.REMOTE_DOMAIN ) )
root.putChild( 'petition', ServerServerResources.HydrusResourceRestrictedPetition( self._service, HydrusServer.REMOTE_DOMAIN ) )
root.putChild( 'update', ServerServerResources.HydrusResourceRestrictedUpdate( self._service, HydrusServer.REMOTE_DOMAIN ) )
#root.putChild( 'immediate_update', ServerServerResources.HydrusResourceRestrictedImmediateUpdate( self._service, HydrusServer.REMOTE_DOMAIN ) )
root.putChild( 'metadata', ServerServerResources.HydrusResourceRestrictedMetadataUpdate( self._service, HydrusServer.REMOTE_DOMAIN ) )

View File

@ -306,8 +306,8 @@ class HydrusResourceRestrictedPetition( HydrusResourceRestricted ):
def _threadDoGETJob( self, request ):
content_type = request.request_args[ 'content_type' ]
status = request.request_args[ 'status' ]
content_type = request.hydrus_args[ 'content_type' ]
status = request.hydrus_args[ 'status' ]
petition = HydrusGlobals.server_controller.Read( 'petition', self._service_key, request.hydrus_account, content_type, status )
@ -324,7 +324,15 @@ class HydrusResourceRestrictedRegistrationKeys( HydrusResourceRestricted ):
num = request.hydrus_args[ 'num' ]
account_type_key = request.hydrus_args[ 'account_type_key' ]
expires = request.hydrus_args[ 'expires' ]
if 'expires' in request.hydrus_args:
expires = request.hydrus_args[ 'expires' ]
else:
expires = None
registration_keys = HydrusGlobals.server_controller.Read( 'registration_keys', self._service_key, request.hydrus_account, num, account_type_key, expires )

View File

@ -1,9 +1,13 @@
import ClientConstants as CC
import ClientData
import ClientSearch
import HydrusConstants as HC
import HydrusData
import HydrusNetwork
import HydrusSerialisable
import os
import unittest
import wx
class TestServer( unittest.TestCase ):
@ -58,6 +62,18 @@ class TestServer( unittest.TestCase ):
def test_basics( self ):
def test( obj, dupe_obj ):
self.assertEqual( len( obj.items() ), len( dupe_obj.items() ) )
for ( key, value ) in obj.items():
self.assertEqual( value, dupe_obj[ key ] )
#
d = HydrusSerialisable.SerialisableDictionary()
d[ 1 ] = 2
@ -71,16 +87,79 @@ class TestServer( unittest.TestCase ):
d[ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'test pred 2' ) ] = HydrusSerialisable.SerialisableList( [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'test' + str( i ) ) for i in range( 10 ) ] )
def test( obj, dupe_obj ):
self.assertEqual( len( d.keys() ), 7 )
for ( key, value ) in d.items():
self.assertEqual( len( obj.items() ), len( dupe_obj.items() ) )
for ( key, value ) in obj.items():
self.assertEqual( value, dupe_obj[ key ] )
self.assertEqual( d[ key ], value )
self._dump_and_load_and_test( d, test )
#
db = HydrusSerialisable.SerialisableBytesDictionary()
db[ HydrusData.GenerateKey() ] = HydrusData.GenerateKey()
db[ HydrusData.GenerateKey() ] = [ HydrusData.GenerateKey() for i in range( 10 ) ]
db[ 1 ] = HydrusData.GenerateKey()
db[ 2 ] = [ HydrusData.GenerateKey() for i in range( 10 ) ]
self.assertEqual( len( db.keys() ), 4 )
for ( key, value ) in db.items():
self.assertEqual( db[ key ], value )
self._dump_and_load_and_test( db, test )
def test_SERIALISABLE_TYPE_SHORTCUTS( self ):
def test( obj, dupe_obj ):
for ( ( modifier, key ), action ) in obj.IterateKeyboardShortcuts():
self.assertEqual( dupe_obj.GetKeyboardAction( modifier, key ), action )
for ( ( modifier, mouse_button ), action ) in obj.IterateMouseShortcuts():
self.assertEqual( dupe_obj.GetMouseAction( modifier, mouse_button ), action )
action_1 = ( HydrusData.GenerateKey(), u'test unicode tag\u2026' )
action_2 = ( HydrusData.GenerateKey(), 0.5 )
action_3 = ( None, 'archive' )
shortcuts = ClientData.Shortcuts( 'test' )
shortcuts.SetKeyboardAction( wx.ACCEL_NORMAL, wx.WXK_NUMPAD1, action_1 )
shortcuts.SetKeyboardAction( wx.ACCEL_SHIFT, wx.WXK_END, action_2 )
shortcuts.SetKeyboardAction( wx.ACCEL_CTRL, ord( 'R' ), action_3 )
shortcuts.SetMouseAction( wx.ACCEL_NORMAL, wx.MOUSE_BTN_LEFT, action_1 )
shortcuts.SetMouseAction( wx.ACCEL_SHIFT, wx.MOUSE_BTN_MIDDLE, action_2 )
shortcuts.SetMouseAction( wx.ACCEL_CTRL, wx.MOUSE_BTN_RIGHT, action_3 )
shortcuts.SetMouseAction( wx.ACCEL_ALT, wx.MOUSE_WHEEL_VERTICAL, action_3 )
k_a = [ item for item in shortcuts.IterateKeyboardShortcuts() ]
self.assertEqual( len( k_a ), 3 )
for ( ( modifier, key ), action ) in k_a:
self.assertEqual( shortcuts.GetKeyboardAction( modifier, key ), action )
m_a = [ item for item in shortcuts.IterateMouseShortcuts() ]
self.assertEqual( len( m_a ), 4 )
for ( ( modifier, mouse_button ), action ) in m_a:
self.assertEqual( shortcuts.GetMouseAction( modifier, mouse_button ), action )