Version 194
|
@ -8,7 +8,10 @@ This github repository is currently a weekly sync with my home dev environment,
|
|||
|
||||
The program can do quite a lot! Please check out the help inside the release or [here](http://hydrusnetwork.github.io/hydrus/help).
|
||||
|
||||
* [Homepage](http://hydrusnetwork.github.io/hydrus/)
|
||||
* [homepage](http://hydrusnetwork.github.io/hydrus/)
|
||||
* [8chan board](https://8ch.net/hydrus/index.html)
|
||||
* [twitter](https://twitter.com/hydrusnetwork)
|
||||
* [tumblr](http://hydrus.tumblr.com/)
|
||||
|
||||
## Attribution
|
||||
|
||||
|
|
|
@ -74,14 +74,14 @@
|
|||
<h3>setting a password</h3>
|
||||
<p>the client offers a very simple password system, enough to keep out noobs. You can set it at <i>database->set a password</i>. It will thereafter ask for the password every time you start the program, and will not open without it. However none of the database is encrypted, and someone with enough enthusiasm or a tool and access to your computer can still very easily see what files you have. The password is mainly to stop idle snoops checking your images if you are away from your machine.</p>
|
||||
<h3>the client's server</h3>
|
||||
<p>The client runs a very simple http server. I want to do much more with it in future.</p>
|
||||
<p>When you boot the client, it will try to host a service on port 45865, which will respond to /file and /thumbnail requests just like a file repository, but without needing an access key, and only to localhost (127.0.0.1).</p>
|
||||
<p>The client can run a very simple http server. It does not run by default, but you can start it by giving it a port at <i>file->options->local server</i>. Once you ok that dialog, the client will try to launch it. You may get a firewall warning.</p>
|
||||
<p>The server responds to /file and /thumbnail requests just like a file repository, but without needing an access key, and only if the request comes from localhost (127.0.0.1).</p>
|
||||
<p>For instance, the following image (6c0ae65894c7a5ffd686f54cc052326b8ea188a691a1895b2f88b7c60a07f13f.jpg, in the help dir) is served here from disk:</p>
|
||||
<p><img src="6c0ae65894c7a5ffd686f54cc052326b8ea188a691a1895b2f88b7c60a07f13f.jpg" /></p>
|
||||
<p>And here it will attempt to load from the client:</p>
|
||||
<p>And here it will attempt to load from the client at port 45865:</p>
|
||||
<p><img src="http://127.0.0.1:45865/file?hash=6c0ae65894c7a5ffd686f54cc052326b8ea188a691a1895b2f88b7c60a07f13f" /></p>
|
||||
<p>For more information, check the image's two urls. It will of course only display in the second case if you import it to the client and have it running when you load this page. You can copy the second image's url and replace the hash with that of any other file in your collection and it should work.</p>
|
||||
<p>If you run multiple copies of the client, their local ports will clash. You can change the port number in options.</p>
|
||||
<p>For more information, check the image's two urls. It will of course only display in the second case if you import the file to your client and have the server running on 45865 when you load this page. You can copy the second image's url and replace the hash with that of any other file in your collection and it should work.</p>
|
||||
<p>I want to do much more with this in future.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -8,6 +8,19 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 194</h3></li>
|
||||
<ul>
|
||||
<li>ipfs pins and unpins can now be queued up like file repository pending and petitioned, through the regular thumbnail right-click menu, which also reports some/all ipfs pinned selection status</li>
|
||||
<li>this ipfs action queue is similarly summarised and commited at the normal service 'pending' menu</li>
|
||||
<li>ipfs's 'pinned', 'to pin', and 'to unpin' statuses are displayed on thumbnails with ipfs-specific icons</li>
|
||||
<li>you can copy the focussed file's ipfs multihash or all the selected files' ipfs multihashes from the thumbnail menu's share->copy->ipfs multihash</li>
|
||||
<li>added a .txt tag parser to the 'path tagging' import dialog--it will parse the same sort of txt files the export dialog produces</li>
|
||||
<li>the client's new 'requests' network code is harmonised, generally improved, and now produces hydrus-compatible exceptions</li>
|
||||
<li>updated help re the local server and boorus now defaulting to off</li>
|
||||
<li>db can now remember service-specific filenames (e.g. ipfs multihashes)</li>
|
||||
<li>cleaned up some overly complicated and confused thumbnail menu code</li>
|
||||
<li>the pending menu now specifies what it is about to do more plainly</li>
|
||||
</ul>
|
||||
<li><h3>version 193</h3></li>
|
||||
<ul>
|
||||
<li>the client's local server and local booru can be turned off from their respective management panels, and from now on, the client will initialise with them this way.</li>
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
<p>The hydrus client has a simple booru to help you share your files with others over the internet.</p>
|
||||
<p>First of all, this is <b>hosted from your client</b>, which means other people will be connecting to your computer and fetching files you choose to share from your hard drive. If you close your client or shut your computer down, the local booru will no longer work.</p>
|
||||
<h3>how to do it</h3>
|
||||
<p>Right click some files you want to share and select <i>share->local booru</i>. This will throw up a small dialog, like so:</p>
|
||||
<p>First of all, turn the local booru server on by going to <i>services->manage services</i> and giving it a port:</p>
|
||||
<p><img src="local_booru_services.png" /></p>
|
||||
<p>It doesn't matter what you pick, but make it something fairly high. When you ok that dialog, the client should start the booru. You may get a firewall warning.</p>
|
||||
<p>Then right click some files you want to share and select <i>share->local booru</i>. This will throw up a small dialog, like so:</p>
|
||||
<p><img src="local_booru_dialog.png" /></p>
|
||||
<p>This lets you enter an optional <i>name</i>, which titles the share and helps you keep track of it, an optional <i>text</i>, which lets you say some words or html to the people you are sharing with, and an <i>expiry</i>, which lets you determine if and when the share will no longer work.</p>
|
||||
<p>You can also copy either the internal or external link to your clipboard. The internal link (usually starting something like http://127.0.0.1:45866/) works inside your network and is great just for testing, while the external link (starting http://[your external ip address]:[external port]/) will work for anyone around the world, <b>as long as your booru's port is being forwarded correctly</b>.</p>
|
||||
|
@ -18,10 +21,8 @@
|
|||
<p class="warning">Note that anyone with the external link will be able to see your share, so make sure you only share links with people you trust.</p>
|
||||
<h3>forwarding your port</h3>
|
||||
<p>Your home router acts as a barrier between the computers inside the network and the internet. Those inside can see out, but outsiders can only see what you tell the router to permit. Since you want to let people connect to your computer, you need to tell the router to forward all requests of a certain kind to your computer, and thus your client.</p>
|
||||
<p>If you have never done this before, it can be a headache, especially doing it manually. Luckily, a technology called UPnP makes it a ton easier, and this is how your Skype or Bittorrent clients do it automatically. Since I'm insane about privacy, the hydrus client will not open any external ports without your permission. You just need to go to <i>services->manage services</i> and uncheck a box:</p>
|
||||
<p><img src="local_booru_upnp.png" /></p>
|
||||
<p>Unless you know what you are doing and have a good reason to make them different, you might as well keep the internal and external ports the same.</p>
|
||||
<p>Once you have it set up, the client should make sure your router keeps that port open for your client. You should see the new mapping appear in your <i>services->manage local upnp</i> dialog, which lists all your router's current port mappings.</p>
|
||||
<p>If you have never done this before, it can be a headache, especially doing it manually. Luckily, a technology called UPnP makes it a ton easier, and this is how your Skype or Bittorrent clients do it automatically. Not all routers support it, but most do. You can have hydrus try to open a port this way back on <i>services->manage services</i>. Unless you know what you are doing and have a good reason to make them different, you might as well keep the internal and external ports the same.</p>
|
||||
<p>Once you have it set up, the client will try to make sure your router keeps that port open for your client. If it all works, you should see the new mapping appear in your <i>services->manage local upnp</i> dialog, which lists all your router's current port mappings.</p>
|
||||
<p>If you want to test that the port forward is set up correctly, going to http://[external ip]:[external port]/ should give a little html just saying hello. Your ISP might not allow you to talk to yourself, though, so ask a friend to try if you are having trouble.</p>
|
||||
<p>If you still do not understand what is going on here, <a href="http://www.howtogeek.com/66214/how-to-forward-ports-on-your-router/">this</a> is a good article explaining everything.</p>
|
||||
<p>If you do not like UPnP or your router does not support it, you can set the port forward up manually, but I encourage you to keep the internal and external port the same, because absent a 'upnp port' option, the 'copy external share link' button will use the internal port.</p>
|
||||
|
|
After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 30 KiB |
|
@ -362,6 +362,9 @@ class GlobalBMPs( object ):
|
|||
GlobalBMPs.file_repository = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'file_repository_small.png' ) )
|
||||
GlobalBMPs.file_repository_pending = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'file_repository_pending_small.png' ) )
|
||||
GlobalBMPs.file_repository_petitioned = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'file_repository_petitioned_small.png' ) )
|
||||
GlobalBMPs.ipfs = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'ipfs_small.png' ) )
|
||||
GlobalBMPs.ipfs_pending = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'ipfs_pending_small.png' ) )
|
||||
GlobalBMPs.ipfs_petitioned = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'ipfs_petitioned_small.png' ) )
|
||||
|
||||
GlobalBMPs.collection = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'collection.png' ) )
|
||||
GlobalBMPs.inbox = wx.Bitmap( os.path.join( HC.STATIC_DIR, 'inbox.png' ) )
|
||||
|
|
|
@ -1622,6 +1622,8 @@ class DB( HydrusDB.HydrusDB ):
|
|||
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._c.execute( 'CREATE TABLE service_filenames ( service_id INTEGER REFERENCES services ON DELETE CASCADE, hash_id INTEGER, filename TEXT, PRIMARY KEY( service_id, hash_id ) );' )
|
||||
|
||||
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 shutdown_timestamps ( shutdown_type INTEGER PRIMARY KEY, timestamp INTEGER );' )
|
||||
|
@ -1925,7 +1927,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
self._c.execute( 'DELETE FROM tag_sibling_petitions WHERE service_id = ?;', ( service_id, ) )
|
||||
self._c.execute( 'DELETE FROM tag_parent_petitions WHERE service_id = ?;', ( service_id, ) )
|
||||
|
||||
elif service.GetServiceType() == HC.FILE_REPOSITORY:
|
||||
elif service.GetServiceType() in ( HC.FILE_REPOSITORY, HC.IPFS ):
|
||||
|
||||
self._c.execute( 'DELETE FROM file_transfers WHERE service_id = ?;', ( service_id, ) )
|
||||
self._c.execute( 'DELETE FROM file_petitions WHERE service_id = ?;', ( service_id, ) )
|
||||
|
@ -3503,7 +3505,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
def _GetNumsPending( self ):
|
||||
|
||||
services = self._GetServices( ( HC.TAG_REPOSITORY, HC.FILE_REPOSITORY ) )
|
||||
services = self._GetServices( ( HC.TAG_REPOSITORY, HC.FILE_REPOSITORY, HC.IPFS ) )
|
||||
|
||||
pendings = {}
|
||||
|
||||
|
@ -3514,7 +3516,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
service_id = self._GetServiceId( service_key )
|
||||
|
||||
if service_type == HC.FILE_REPOSITORY: info_types = { HC.SERVICE_INFO_NUM_PENDING_FILES, HC.SERVICE_INFO_NUM_PETITIONED_FILES }
|
||||
if service_type in ( HC.FILE_REPOSITORY, HC.IPFS ): info_types = { HC.SERVICE_INFO_NUM_PENDING_FILES, HC.SERVICE_INFO_NUM_PETITIONED_FILES }
|
||||
elif service_type == HC.TAG_REPOSITORY: info_types = { HC.SERVICE_INFO_NUM_PENDING_MAPPINGS, HC.SERVICE_INFO_NUM_PETITIONED_MAPPINGS, HC.SERVICE_INFO_NUM_PENDING_TAG_SIBLINGS, HC.SERVICE_INFO_NUM_PETITIONED_TAG_SIBLINGS, HC.SERVICE_INFO_NUM_PENDING_TAG_PARENTS, HC.SERVICE_INFO_NUM_PETITIONED_TAG_PARENTS }
|
||||
|
||||
pendings[ service_key ] = self._GetServiceInfoSpecific( service_id, service_type, info_types )
|
||||
|
@ -3648,6 +3650,32 @@ class DB( HydrusDB.HydrusDB ):
|
|||
content_data_dict[ HC.CONTENT_TYPE_FILES ][ HC.CONTENT_UPDATE_PETITION ] = petitioned
|
||||
|
||||
|
||||
elif service_type == HC.IPFS:
|
||||
|
||||
result = self._c.execute( 'SELECT hash_id FROM file_transfers WHERE service_id = ?;', ( service_id, ) ).fetchone()
|
||||
|
||||
if result is not None:
|
||||
|
||||
( hash_id, ) = result
|
||||
|
||||
( media_result, ) = self._GetMediaResults( CC.LOCAL_FILE_SERVICE_KEY, ( hash_id, ) )
|
||||
|
||||
return media_result
|
||||
|
||||
|
||||
result = self._c.execute( 'SELECT hash_id FROM file_petitions WHERE service_id = ?;', ( service_id, ) ).fetchone()
|
||||
|
||||
if result is not None:
|
||||
|
||||
( hash_id, ) = result
|
||||
|
||||
hash = self._GetHash( hash_id )
|
||||
|
||||
multihash = self._GetServiceFilename( service_id, hash_id )
|
||||
|
||||
return ( hash, multihash )
|
||||
|
||||
|
||||
|
||||
|
||||
if len( content_data_dict ) > 0:
|
||||
|
@ -3729,6 +3757,32 @@ class DB( HydrusDB.HydrusDB ):
|
|||
return service
|
||||
|
||||
|
||||
def _GetServiceFilename( self, service_id, hash_id ):
|
||||
|
||||
result = self._c.execute( 'SELECT filename FROM service_filenames WHERE service_id = ? AND hash_id = ?;', ( service_id, hash_id ) ).fetchone()
|
||||
|
||||
if result is None:
|
||||
|
||||
raise HydrusExceptions.DataMissing( 'Service filename not found!' )
|
||||
|
||||
|
||||
( filename, ) = result
|
||||
|
||||
return filename
|
||||
|
||||
|
||||
def _GetServiceFilenames( self, service_key, hashes ):
|
||||
|
||||
service_id = self._GetServiceId( service_key )
|
||||
hash_ids = self._GetHashIds( hashes )
|
||||
|
||||
result = [ filename for ( filename, ) in self._c.execute( 'SELECT filename FROM service_filenames WHERE service_id = ? AND hash_id IN ' + HydrusData.SplayListForDB( hash_ids ) + ';', ( service_id, ) ) ]
|
||||
|
||||
result.sort()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _GetServices( self, limited_types = HC.ALL_SERVICES ):
|
||||
|
||||
service_ids = [ service_id for ( service_id, ) in self._c.execute( 'SELECT service_id FROM services WHERE service_type IN ' + HydrusData.SplayListForDB( limited_types ) + ';' ) ]
|
||||
|
@ -3770,6 +3824,10 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_TOTAL_SIZE, HC.SERVICE_INFO_NUM_DELETED_FILES, HC.SERVICE_INFO_NUM_THUMBNAILS, HC.SERVICE_INFO_NUM_THUMBNAILS_LOCAL }
|
||||
|
||||
elif service_type == HC.IPFS:
|
||||
|
||||
info_types = { HC.SERVICE_INFO_NUM_FILES }
|
||||
|
||||
elif service_type == HC.LOCAL_TAG:
|
||||
|
||||
info_types = { HC.SERVICE_INFO_NUM_FILES, HC.SERVICE_INFO_NUM_NAMESPACES, HC.SERVICE_INFO_NUM_TAGS, HC.SERVICE_INFO_NUM_MAPPINGS }
|
||||
|
@ -3830,7 +3888,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
save_it = True
|
||||
|
||||
if service_type in ( HC.LOCAL_FILE, HC.FILE_REPOSITORY ):
|
||||
if service_type in ( HC.LOCAL_FILE, HC.FILE_REPOSITORY, HC.IPFS ):
|
||||
|
||||
if info_type in ( HC.SERVICE_INFO_NUM_PENDING_FILES, HC.SERVICE_INFO_NUM_PETITIONED_FILES ): save_it = False
|
||||
|
||||
|
@ -4522,7 +4580,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
( data_type, action, row ) = content_update.ToTuple()
|
||||
|
||||
if service_type in ( HC.FILE_REPOSITORY, HC.LOCAL_FILE ):
|
||||
if service_type in ( HC.FILE_REPOSITORY, HC.LOCAL_FILE, HC.IPFS ):
|
||||
|
||||
if data_type == HC.CONTENT_TYPE_FILES:
|
||||
|
||||
|
@ -4539,11 +4597,24 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
elif action == HC.CONTENT_UPDATE_ADD:
|
||||
|
||||
( hash, size, mime, timestamp, width, height, duration, num_frames, num_words ) = row
|
||||
|
||||
hash_id = self._GetHashId( hash )
|
||||
|
||||
self._AddFilesInfo( [ ( hash_id, size, mime, width, height, duration, num_frames, num_words ) ] )
|
||||
if service_type in ( HC.FILE_REPOSITORY, HC.LOCAL_FILE ):
|
||||
|
||||
( hash, size, mime, timestamp, width, height, duration, num_frames, num_words ) = row
|
||||
|
||||
hash_id = self._GetHashId( hash )
|
||||
|
||||
self._AddFilesInfo( [ ( hash_id, size, mime, width, height, duration, num_frames, num_words ) ] )
|
||||
|
||||
elif service_type == HC.IPFS:
|
||||
|
||||
( hash, multihash ) = row
|
||||
|
||||
hash_id = self._GetHashId( hash )
|
||||
|
||||
self._SetServiceFilename( service_id, hash_id, multihash )
|
||||
|
||||
timestamp = HydrusData.GetNow()
|
||||
|
||||
|
||||
self._AddFiles( service_id, [ ( hash_id, timestamp ) ] )
|
||||
|
||||
|
@ -5129,6 +5200,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
elif action == 'serialisable': result = self._GetJSONDump( *args, **kwargs )
|
||||
elif action == 'serialisable_named': result = self._GetJSONDumpNamed( *args, **kwargs )
|
||||
elif action == 'serialisable_names': result = self._GetJSONDumpNames( *args, **kwargs )
|
||||
elif action == 'service_filenames': result = self._GetServiceFilenames( *args, **kwargs )
|
||||
elif action == 'local_booru_share_keys': result = self._GetYAMLDumpNames( YAML_DUMP_ID_LOCAL_BOORU )
|
||||
elif action == 'local_booru_share': result = self._GetYAMLDump( YAML_DUMP_ID_LOCAL_BOORU, *args, **kwargs )
|
||||
elif action == 'local_booru_shares': result = self._GetYAMLDump( YAML_DUMP_ID_LOCAL_BOORU )
|
||||
|
@ -5354,6 +5426,11 @@ class DB( HydrusDB.HydrusDB ):
|
|||
self._SaveOptions( options )
|
||||
|
||||
|
||||
def _SetServiceFilename( self, service_id, hash_id, filename ):
|
||||
|
||||
self._c.execute( 'REPLACE INTO service_filenames ( service_id, hash_id, filename ) VALUES ( ?, ?, ? );', ( service_id, hash_id, filename ) )
|
||||
|
||||
|
||||
def _SetTagCensorship( self, info ):
|
||||
|
||||
self._c.execute( 'DELETE FROM tag_censorship;' )
|
||||
|
@ -6383,6 +6460,11 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
|
||||
|
||||
if version == 193:
|
||||
|
||||
self._c.execute( 'CREATE TABLE service_filenames ( service_id INTEGER REFERENCES services ON DELETE CASCADE, hash_id INTEGER, filename TEXT, PRIMARY KEY( service_id, hash_id ) );' )
|
||||
|
||||
|
||||
self._controller.pub( 'splash_set_title_text', 'updating db to v' + str( version + 1 ) )
|
||||
|
||||
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
|
||||
|
|
|
@ -1837,18 +1837,11 @@ class ServiceIPFS( ServiceRemote ):
|
|||
|
||||
url = 'http://' + host + ':' + str( port ) + path
|
||||
|
||||
response = requests.get( url )
|
||||
response = ClientNetworking.RequestsGet( url )
|
||||
|
||||
if response.ok:
|
||||
|
||||
j = response.json()
|
||||
|
||||
return j[ 'Version' ]
|
||||
|
||||
else:
|
||||
|
||||
raise Exception( response.content )
|
||||
|
||||
j = response.json()
|
||||
|
||||
return j[ 'Version' ]
|
||||
|
||||
|
||||
def ImportFile( self, multihash ):
|
||||
|
@ -1868,9 +1861,7 @@ class ServiceIPFS( ServiceRemote ):
|
|||
HydrusGlobals.client_controller.CallToThread( ClientDownloading.THREADDownloadURL, job_key, url, url_string )
|
||||
|
||||
|
||||
def PinFile( self, path ):
|
||||
|
||||
mime = HydrusFileHandling.GetMime( path )
|
||||
def PinFile( self, hash, mime ):
|
||||
|
||||
mime_string = HC.mime_string_lookup[ mime ]
|
||||
|
||||
|
@ -1880,51 +1871,30 @@ class ServiceIPFS( ServiceRemote ):
|
|||
|
||||
url = 'http://' + host + ':' + str( port ) + '/api/v0/add'
|
||||
|
||||
files = { 'path' : ( path, open( path, 'rb' ), mime_string ) }
|
||||
client_files_manager = HydrusGlobals.client_controller.GetClientFilesManager()
|
||||
|
||||
response = requests.put( url, files = files )
|
||||
path = client_files_manager.GetFilePath( hash, mime )
|
||||
|
||||
if response.ok:
|
||||
|
||||
# responds with some json with name and key (ipfs hash)
|
||||
# parse that multihash, wrap it into the content update
|
||||
|
||||
pass # spin off a content update
|
||||
|
||||
else:
|
||||
|
||||
raise Exception( response.content )
|
||||
|
||||
files = { 'path' : ( hash.encode( 'hex' ), open( path, 'rb' ), mime_string ) }
|
||||
|
||||
|
||||
def SyncPinned( self ):
|
||||
response = ClientNetworking.RequestsPost( url, files = files )
|
||||
|
||||
# query pin ls and update private cache with what we have
|
||||
# add a button for this on review services, I think
|
||||
j = response.json()
|
||||
|
||||
pass
|
||||
multihash = j[ 'Hash' ]
|
||||
|
||||
return multihash
|
||||
|
||||
|
||||
def UnpinFile( self, multihash ):
|
||||
|
||||
# will have to get multihash from db
|
||||
|
||||
credentials = self.GetCredentials()
|
||||
|
||||
( host, port ) = credentials.GetAddress()
|
||||
|
||||
url = 'http://' + host + ':' + str( port ) + '/api/v0/pin/rm/' + multihash
|
||||
|
||||
response = requests.get( url )
|
||||
|
||||
if response.ok:
|
||||
|
||||
pass # spin off a content update
|
||||
|
||||
else:
|
||||
|
||||
raise Exception( response.content )
|
||||
|
||||
ClientNetworking.RequestsGet( url )
|
||||
|
||||
|
||||
class Shortcuts( HydrusSerialisable.SerialisableBaseNamed ):
|
||||
|
|
|
@ -230,51 +230,48 @@ def THREADDownloadURL( job_key, url, url_string ):
|
|||
|
||||
try:
|
||||
|
||||
response = requests.get( url, stream = True )
|
||||
response = ClientNetworking.RequestsGet( url, stream = True )
|
||||
|
||||
if response.ok:
|
||||
if 'content-length' in response.headers:
|
||||
|
||||
if 'content-length' in response.headers:
|
||||
|
||||
gauge_range = int( response.headers[ 'content-length' ] )
|
||||
|
||||
else:
|
||||
|
||||
gauge_range = None
|
||||
|
||||
|
||||
gauge_value = 0
|
||||
|
||||
with open( temp_path, 'wb' ) as f:
|
||||
|
||||
for chunk in response.iter_content( chunk_size = 65536 ):
|
||||
|
||||
( i_paused, should_quit ) = job_key.WaitIfNeeded()
|
||||
|
||||
if should_quit:
|
||||
|
||||
return
|
||||
|
||||
|
||||
f.write( chunk )
|
||||
|
||||
gauge_value += len( chunk )
|
||||
|
||||
hook( gauge_value, gauge_range )
|
||||
|
||||
|
||||
|
||||
job_key.DeleteVariable( 'popup_gauge_1' )
|
||||
job_key.SetVariable( 'popup_text_1', 'importing ' + url_string )
|
||||
|
||||
( result, hash ) = HydrusGlobals.client_controller.WriteSynchronous( 'import_file', temp_path )
|
||||
gauge_range = int( response.headers[ 'content-length' ] )
|
||||
|
||||
else:
|
||||
|
||||
job_key.Cancel()
|
||||
gauge_range = None
|
||||
|
||||
raise HydrusExceptions.NetworkException( response.content )
|
||||
|
||||
gauge_value = 0
|
||||
|
||||
with open( temp_path, 'wb' ) as f:
|
||||
|
||||
for chunk in response.iter_content( chunk_size = 65536 ):
|
||||
|
||||
( i_paused, should_quit ) = job_key.WaitIfNeeded()
|
||||
|
||||
if should_quit:
|
||||
|
||||
return
|
||||
|
||||
|
||||
f.write( chunk )
|
||||
|
||||
gauge_value += len( chunk )
|
||||
|
||||
hook( gauge_value, gauge_range )
|
||||
|
||||
|
||||
|
||||
job_key.DeleteVariable( 'popup_gauge_1' )
|
||||
job_key.SetVariable( 'popup_text_1', 'importing ' + url_string )
|
||||
|
||||
( result, hash ) = HydrusGlobals.client_controller.WriteSynchronous( 'import_file', temp_path )
|
||||
|
||||
except HydrusExceptions.NetworkException:
|
||||
|
||||
job_key.Cancel()
|
||||
|
||||
raise
|
||||
|
||||
finally:
|
||||
|
||||
|
|
|
@ -439,7 +439,7 @@ class ExportFolder( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
if HydrusData.TimeHasPassed( self._last_checked + self._period ):
|
||||
|
||||
folder_path = self._name
|
||||
folder_path = HydrusData.ToUnicode( self._name )
|
||||
|
||||
if os.path.exists( folder_path ) and os.path.isdir( folder_path ):
|
||||
|
||||
|
@ -477,7 +477,7 @@ class ExportFolder( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
terms = ParseExportPhrase( self._phrase )
|
||||
|
||||
previous_filenames = set( os.listdir( HydrusData.ToUnicode( folder_path ) ) )
|
||||
previous_filenames = set( os.listdir( folder_path ) )
|
||||
|
||||
sync_filenames = set()
|
||||
|
||||
|
|
|
@ -886,12 +886,28 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
|||
service_type = service.GetServiceType()
|
||||
name = service.GetName()
|
||||
|
||||
if service_type == HC.TAG_REPOSITORY:
|
||||
|
||||
pending_phrase = 'mappings to upload'
|
||||
petitioned_phrase = 'mappings to petition'
|
||||
|
||||
elif service_type == HC.FILE_REPOSITORY:
|
||||
|
||||
pending_phrase = 'files to upload'
|
||||
petitioned_phrase = 'files to petition'
|
||||
|
||||
elif service_type == HC.IPFS:
|
||||
|
||||
pending_phrase = 'files to pin'
|
||||
petitioned_phrase = 'files to unpin'
|
||||
|
||||
|
||||
if service_type == HC.TAG_REPOSITORY:
|
||||
|
||||
num_pending = info[ HC.SERVICE_INFO_NUM_PENDING_MAPPINGS ] + info[ HC.SERVICE_INFO_NUM_PENDING_TAG_SIBLINGS ] + info[ HC.SERVICE_INFO_NUM_PENDING_TAG_PARENTS ]
|
||||
num_petitioned = info[ HC.SERVICE_INFO_NUM_PETITIONED_MAPPINGS ] + info[ HC.SERVICE_INFO_NUM_PETITIONED_TAG_SIBLINGS ] + info[ HC.SERVICE_INFO_NUM_PETITIONED_TAG_PARENTS ]
|
||||
|
||||
elif service_type == HC.FILE_REPOSITORY:
|
||||
elif service_type in ( HC.FILE_REPOSITORY, HC.IPFS ):
|
||||
|
||||
num_pending = info[ HC.SERVICE_INFO_NUM_PENDING_FILES ]
|
||||
num_petitioned = info[ HC.SERVICE_INFO_NUM_PETITIONED_FILES ]
|
||||
|
@ -901,10 +917,24 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
|||
|
||||
submenu = wx.Menu()
|
||||
|
||||
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'upload_pending', service_key ), p( '&Upload' ), p( 'Upload ' + name + '\'s Pending and Petitions.' ) )
|
||||
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'delete_pending', service_key ), p( '&Forget' ), p( 'Clear ' + name + '\'s Pending and Petitions.' ) )
|
||||
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'upload_pending', service_key ), p( '&Commit' ), p( 'Upload ' + name + '\'s pending content.' ) )
|
||||
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'delete_pending', service_key ), p( '&Forget' ), p( 'Clear ' + name + '\'s pending content.' ) )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, p( name + ' Pending (' + HydrusData.ConvertValueRangeToPrettyString( num_pending, num_petitioned ) + ')' ), submenu )
|
||||
submessages = []
|
||||
|
||||
if num_pending > 0:
|
||||
|
||||
submessages.append( HydrusData.ConvertIntToPrettyString( num_pending ) + ' ' + pending_phrase )
|
||||
|
||||
|
||||
if num_petitioned > 0:
|
||||
|
||||
submessages.append( HydrusData.ConvertIntToPrettyString( num_petitioned ) + ' ' + petitioned_phrase )
|
||||
|
||||
|
||||
message = name + ': ' + ', '.join( submessages )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, p( message ), submenu )
|
||||
|
||||
|
||||
total_num_pending += num_pending + num_petitioned
|
||||
|
@ -2019,36 +2049,63 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
try:
|
||||
|
||||
if isinstance( result, ClientMedia.MediaResult ):
|
||||
if service_type in HC.REPOSITORIES:
|
||||
|
||||
media_result = result
|
||||
if isinstance( result, ClientMedia.MediaResult ):
|
||||
|
||||
media_result = result
|
||||
|
||||
client_files_manager = self._controller.GetClientFilesManager()
|
||||
|
||||
hash = media_result.GetHash()
|
||||
mime = media_result.GetMime()
|
||||
|
||||
path = client_files_manager.GetFilePath( hash, mime )
|
||||
|
||||
with open( path, 'rb' ) as f: file = f.read()
|
||||
|
||||
service.Request( HC.POST, 'file', { 'file' : file } )
|
||||
|
||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = media_result.ToTuple()
|
||||
|
||||
timestamp = HydrusData.GetNow()
|
||||
|
||||
content_update_row = ( hash, size, mime, timestamp, width, height, duration, num_frames, num_words )
|
||||
|
||||
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_ADD, content_update_row ) ]
|
||||
|
||||
else:
|
||||
|
||||
content_update_package = result
|
||||
|
||||
service.Request( HC.POST, 'content_update_package', { 'update' : content_update_package } )
|
||||
|
||||
content_updates = content_update_package.GetContentUpdates( for_client = True )
|
||||
|
||||
|
||||
client_files_manager = self._controller.GetClientFilesManager()
|
||||
elif service_type == HC.IPFS:
|
||||
|
||||
hash = media_result.GetHash()
|
||||
mime = media_result.GetMime()
|
||||
|
||||
path = client_files_manager.GetFilePath( hash, mime )
|
||||
|
||||
with open( path, 'rb' ) as f: file = f.read()
|
||||
|
||||
service.Request( HC.POST, 'file', { 'file' : file } )
|
||||
|
||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = media_result.ToTuple()
|
||||
|
||||
timestamp = HydrusData.GetNow()
|
||||
|
||||
content_update_row = ( hash, size, mime, timestamp, width, height, duration, num_frames, num_words )
|
||||
|
||||
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_ADD, content_update_row ) ]
|
||||
|
||||
else:
|
||||
|
||||
content_update_package = result
|
||||
|
||||
service.Request( HC.POST, 'content_update_package', { 'update' : content_update_package } )
|
||||
|
||||
content_updates = content_update_package.GetContentUpdates( for_client = True )
|
||||
if isinstance( result, ClientMedia.MediaResult ):
|
||||
|
||||
media_result = result
|
||||
|
||||
hash = media_result.GetHash()
|
||||
mime = media_result.GetMime()
|
||||
|
||||
multihash = service.PinFile( hash, mime )
|
||||
|
||||
content_update_row = ( hash, multihash )
|
||||
|
||||
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_ADD, content_update_row ) ]
|
||||
|
||||
else:
|
||||
|
||||
( hash, multihash ) = result
|
||||
|
||||
service.UnpinFile( multihash )
|
||||
|
||||
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, { hash } ) ]
|
||||
|
||||
|
||||
|
||||
self._controller.WriteSynchronous( 'content_updates', { service_key : content_updates } )
|
||||
|
|
|
@ -1388,13 +1388,13 @@ class CanvasWithDetails( Canvas ):
|
|||
|
||||
# repo strings
|
||||
|
||||
file_repo_strings = self._current_media.GetLocationsManager().GetFileRepositoryStrings()
|
||||
remote_strings = self._current_media.GetLocationsManager().GetRemoteLocationStrings()
|
||||
|
||||
for file_repo_string in file_repo_strings:
|
||||
for remote_string in remote_strings:
|
||||
|
||||
( text_width, text_height ) = dc.GetTextExtent( file_repo_string )
|
||||
( text_width, text_height ) = dc.GetTextExtent( remote_string )
|
||||
|
||||
dc.DrawText( file_repo_string, client_width - text_width - 3, current_y )
|
||||
dc.DrawText( remote_string, client_width - text_width - 3, current_y )
|
||||
|
||||
current_y += text_height + 4
|
||||
|
||||
|
|
|
@ -2087,56 +2087,45 @@ class DialogInputShortcut( Dialog ):
|
|||
|
||||
def __init__( self, parent, modifier = wx.ACCEL_NORMAL, key = wx.WXK_F7, action = 'new_page' ):
|
||||
|
||||
self._action = action
|
||||
|
||||
def InitialiseControls():
|
||||
|
||||
self._shortcut = ClientGUICommon.Shortcut( self, modifier, key )
|
||||
|
||||
self._actions = wx.Choice( self, choices = [ 'archive', 'inbox', 'close_page', 'filter', 'fullscreen_switch', 'frame_back', 'frame_next', 'manage_ratings', 'manage_tags', 'new_page', 'refresh', 'set_search_focus', 'show_hide_splitters', 'synchronised_wait_switch', 'previous', 'next', 'first', 'last', 'undo', 'redo', 'open_externally' ] )
|
||||
|
||||
self._ok = wx.Button( self, id= wx.ID_OK, label = 'Ok' )
|
||||
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'Cancel' )
|
||||
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
|
||||
def PopulateControls():
|
||||
|
||||
self._actions.SetSelection( self._actions.FindString( action ) )
|
||||
|
||||
|
||||
def ArrangeControls():
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
hbox.AddF( self._shortcut, CC.FLAGS_MIXED )
|
||||
hbox.AddF( self._actions, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
b_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||
b_box.AddF( self._ok, CC.FLAGS_MIXED )
|
||||
b_box.AddF( self._cancel, CC.FLAGS_MIXED )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.AddF( b_box, CC.FLAGS_BUTTON_SIZER )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
( x, y ) = self.GetEffectiveMinSize()
|
||||
|
||||
self.SetInitialSize( ( x, y ) )
|
||||
|
||||
|
||||
Dialog.__init__( self, parent, 'configure shortcut' )
|
||||
|
||||
InitialiseControls()
|
||||
self._action = action
|
||||
|
||||
PopulateControls()
|
||||
self._shortcut = ClientGUICommon.Shortcut( self, modifier, key )
|
||||
|
||||
ArrangeControls()
|
||||
self._actions = wx.Choice( self, choices = [ 'archive', 'inbox', 'close_page', 'filter', 'fullscreen_switch', 'frame_back', 'frame_next', 'manage_ratings', 'manage_tags', 'new_page', 'refresh', 'set_search_focus', 'show_hide_splitters', 'synchronised_wait_switch', 'previous', 'next', 'first', 'last', 'undo', 'redo', 'open_externally' ] )
|
||||
|
||||
self._ok = wx.Button( self, id= wx.ID_OK, label = 'Ok' )
|
||||
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'Cancel' )
|
||||
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
#
|
||||
|
||||
self._actions.SetSelection( self._actions.FindString( action ) )
|
||||
|
||||
#
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
hbox.AddF( self._shortcut, CC.FLAGS_MIXED )
|
||||
hbox.AddF( self._actions, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
b_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||
b_box.AddF( self._ok, CC.FLAGS_MIXED )
|
||||
b_box.AddF( self._cancel, CC.FLAGS_MIXED )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.AddF( b_box, CC.FLAGS_BUTTON_SIZER )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
( x, y ) = self.GetEffectiveMinSize()
|
||||
|
||||
self.SetInitialSize( ( x, y ) )
|
||||
|
||||
wx.CallAfter( self._ok.SetFocus )
|
||||
|
||||
|
@ -2915,80 +2904,69 @@ class DialogPathsToTags( Dialog ):
|
|||
|
||||
def __init__( self, parent, paths ):
|
||||
|
||||
def InitialiseControls():
|
||||
|
||||
self._tag_repositories = ClientGUICommon.ListBook( self )
|
||||
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
|
||||
|
||||
self._add_button = wx.Button( self, id = wx.ID_OK, label = 'Import Files' )
|
||||
self._add_button.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'Back to File Selection' )
|
||||
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
|
||||
def PopulateControls():
|
||||
|
||||
services = HydrusGlobals.client_controller.GetServicesManager().GetServices( ( HC.TAG_REPOSITORY, ) )
|
||||
|
||||
for service in services:
|
||||
|
||||
account = service.GetInfo( 'account' )
|
||||
|
||||
if account.HasPermission( HC.POST_DATA ) or account.IsUnknownAccount():
|
||||
|
||||
service_key = service.GetServiceKey()
|
||||
|
||||
name = service.GetName()
|
||||
|
||||
self._tag_repositories.AddPageArgs( name, self._Panel, ( self._tag_repositories, service_key, paths ), {} )
|
||||
|
||||
|
||||
|
||||
page = self._Panel( self._tag_repositories, CC.LOCAL_TAG_SERVICE_KEY, paths )
|
||||
|
||||
name = CC.LOCAL_TAG_SERVICE_KEY
|
||||
|
||||
self._tag_repositories.AddPage( name, page )
|
||||
|
||||
default_tag_repository_key = HC.options[ 'default_tag_repository' ]
|
||||
|
||||
default_tag_repository = HydrusGlobals.client_controller.GetServicesManager().GetService( default_tag_repository_key )
|
||||
|
||||
self._tag_repositories.Select( default_tag_repository.GetName() )
|
||||
|
||||
|
||||
def ArrangeControls():
|
||||
|
||||
buttons = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
buttons.AddF( self._add_button, CC.FLAGS_SMALL_INDENT )
|
||||
buttons.AddF( self._cancel, CC.FLAGS_SMALL_INDENT )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._tag_repositories, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( buttons, CC.FLAGS_BUTTON_SIZER )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
( width, height ) = self.GetMinSize()
|
||||
|
||||
width = max( width, 930 )
|
||||
height = max( height, 680 )
|
||||
|
||||
self.SetInitialSize( ( width, height ) )
|
||||
|
||||
|
||||
Dialog.__init__( self, parent, 'path tagging' )
|
||||
|
||||
self._paths = paths
|
||||
|
||||
InitialiseControls()
|
||||
self._tag_repositories = ClientGUICommon.ListBook( self )
|
||||
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
|
||||
|
||||
PopulateControls()
|
||||
self._add_button = wx.Button( self, id = wx.ID_OK, label = 'Import Files' )
|
||||
self._add_button.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
ArrangeControls()
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'Back to File Selection' )
|
||||
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
#
|
||||
|
||||
services = HydrusGlobals.client_controller.GetServicesManager().GetServices( ( HC.TAG_REPOSITORY, ) )
|
||||
|
||||
for service in services:
|
||||
|
||||
account = service.GetInfo( 'account' )
|
||||
|
||||
if account.HasPermission( HC.POST_DATA ) or account.IsUnknownAccount():
|
||||
|
||||
service_key = service.GetServiceKey()
|
||||
|
||||
name = service.GetName()
|
||||
|
||||
self._tag_repositories.AddPageArgs( name, self._Panel, ( self._tag_repositories, service_key, paths ), {} )
|
||||
|
||||
|
||||
|
||||
page = self._Panel( self._tag_repositories, CC.LOCAL_TAG_SERVICE_KEY, paths )
|
||||
|
||||
name = CC.LOCAL_TAG_SERVICE_KEY
|
||||
|
||||
self._tag_repositories.AddPage( name, page )
|
||||
|
||||
default_tag_repository_key = HC.options[ 'default_tag_repository' ]
|
||||
|
||||
default_tag_repository = HydrusGlobals.client_controller.GetServicesManager().GetService( default_tag_repository_key )
|
||||
|
||||
self._tag_repositories.Select( default_tag_repository.GetName() )
|
||||
|
||||
#
|
||||
|
||||
buttons = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
buttons.AddF( self._add_button, CC.FLAGS_SMALL_INDENT )
|
||||
buttons.AddF( self._cancel, CC.FLAGS_SMALL_INDENT )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._tag_repositories, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( buttons, CC.FLAGS_BUTTON_SIZER )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
( width, height ) = self.GetMinSize()
|
||||
|
||||
width = max( width, 930 )
|
||||
height = max( height, 680 )
|
||||
|
||||
self.SetInitialSize( ( width, height ) )
|
||||
|
||||
interested_actions = [ 'set_search_focus' ]
|
||||
|
||||
|
@ -3048,177 +3026,173 @@ class DialogPathsToTags( Dialog ):
|
|||
|
||||
def __init__( self, parent, service_key, paths ):
|
||||
|
||||
def InitialiseControls():
|
||||
|
||||
self._paths_list = ClientGUICommon.SaneListCtrl( self, 250, [ ( '#', 50 ), ( 'path', 400 ), ( 'tags', -1 ) ] )
|
||||
|
||||
self._paths_list.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
|
||||
self._paths_list.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
|
||||
|
||||
#
|
||||
|
||||
self._quick_namespaces_panel = ClientGUICommon.StaticBox( self, 'quick namespaces' )
|
||||
|
||||
self._quick_namespaces_list = ClientGUICommon.SaneListCtrl( self._quick_namespaces_panel, 200, [ ( 'namespace', 80 ), ( 'regex', -1 ) ], delete_key_callback = self.DeleteQuickNamespaces )
|
||||
|
||||
self._add_quick_namespace_button = wx.Button( self._quick_namespaces_panel, label = 'add' )
|
||||
self._add_quick_namespace_button.Bind( wx.EVT_BUTTON, self.EventAddQuickNamespace )
|
||||
self._add_quick_namespace_button.SetMinSize( ( 20, -1 ) )
|
||||
|
||||
self._edit_quick_namespace_button = wx.Button( self._quick_namespaces_panel, label = 'edit' )
|
||||
self._edit_quick_namespace_button.Bind( wx.EVT_BUTTON, self.EventEditQuickNamespace )
|
||||
self._edit_quick_namespace_button.SetMinSize( ( 20, -1 ) )
|
||||
|
||||
self._delete_quick_namespace_button = wx.Button( self._quick_namespaces_panel, label = 'delete' )
|
||||
self._delete_quick_namespace_button.Bind( wx.EVT_BUTTON, self.EventDeleteQuickNamespace )
|
||||
self._delete_quick_namespace_button.SetMinSize( ( 20, -1 ) )
|
||||
|
||||
#
|
||||
|
||||
self._regexes_panel = ClientGUICommon.StaticBox( self, 'regexes' )
|
||||
|
||||
self._regexes = wx.ListBox( self._regexes_panel )
|
||||
self._regexes.Bind( wx.EVT_LISTBOX_DCLICK, self.EventRemoveRegex )
|
||||
|
||||
self._regex_box = wx.TextCtrl( self._regexes_panel, style=wx.TE_PROCESS_ENTER )
|
||||
self._regex_box.Bind( wx.EVT_TEXT_ENTER, self.EventAddRegex )
|
||||
|
||||
self._regex_shortcuts = ClientGUICommon.RegexButton( self._regexes_panel )
|
||||
|
||||
self._regex_link = wx.HyperlinkCtrl( self._regexes_panel, id = -1, label = 'a good regex introduction', url = 'http://www.aivosto.com/vbtips/regex.html' )
|
||||
|
||||
#
|
||||
|
||||
self._num_panel = ClientGUICommon.StaticBox( self, '#' )
|
||||
|
||||
self._num_base = wx.SpinCtrl( self._num_panel, min = -10000000, max = 10000000, size = ( 60, -1 ) )
|
||||
self._num_base.SetValue( 1 )
|
||||
self._num_base.Bind( wx.EVT_SPINCTRL, self.EventRecalcNum )
|
||||
|
||||
self._num_step = wx.SpinCtrl( self._num_panel, min = -1000000, max = 1000000, size = ( 60, -1 ) )
|
||||
self._num_step.SetValue( 1 )
|
||||
self._num_step.Bind( wx.EVT_SPINCTRL, self.EventRecalcNum )
|
||||
|
||||
self._num_namespace = wx.TextCtrl( self._num_panel, size = ( 100, -1 ) )
|
||||
self._num_namespace.Bind( wx.EVT_TEXT, self.EventNumNamespaceChanged )
|
||||
|
||||
#
|
||||
|
||||
self._tags_panel = ClientGUICommon.StaticBox( self, 'tags for all' )
|
||||
|
||||
self._tags = ClientGUICommon.ListBoxTagsStrings( self._tags_panel, self.TagsRemoved )
|
||||
|
||||
expand_parents = True
|
||||
|
||||
self._tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self._tags_panel, self.EnterTags, expand_parents, CC.LOCAL_FILE_SERVICE_KEY, service_key )
|
||||
|
||||
#
|
||||
|
||||
self._single_tags_panel = ClientGUICommon.StaticBox( self, 'tags just for selected files' )
|
||||
|
||||
self._paths_to_single_tags = collections.defaultdict( set )
|
||||
|
||||
self._single_tags = ClientGUICommon.ListBoxTagsStrings( self._single_tags_panel, self.SingleTagsRemoved )
|
||||
|
||||
expand_parents = True
|
||||
|
||||
self._single_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self._single_tags_panel, self.EnterTagsSingle, expand_parents, CC.LOCAL_FILE_SERVICE_KEY, service_key )
|
||||
|
||||
|
||||
def PopulateControls():
|
||||
|
||||
num_base = self._num_base.GetValue()
|
||||
num_step = self._num_step.GetValue()
|
||||
|
||||
for ( num, path ) in enumerate( self._paths ):
|
||||
|
||||
processed_num = num_base + num * num_step
|
||||
|
||||
pretty_num = HydrusData.ConvertIntToPrettyString( processed_num )
|
||||
|
||||
tags = self._GetTags( num, path )
|
||||
|
||||
tags_string = ', '.join( tags )
|
||||
|
||||
self._paths_list.Append( ( pretty_num, path, tags_string ), ( ( num, processed_num ), path, tags ) )
|
||||
|
||||
|
||||
self._single_tag_box.Disable()
|
||||
|
||||
|
||||
def ArrangeControls():
|
||||
|
||||
button_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
button_box.AddF( self._add_quick_namespace_button, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
button_box.AddF( self._edit_quick_namespace_button, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
button_box.AddF( self._delete_quick_namespace_button, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self._quick_namespaces_panel.AddF( self._quick_namespaces_list, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._quick_namespaces_panel.AddF( button_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
self._regexes_panel.AddF( self._regexes, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._regexes_panel.AddF( self._regex_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._regexes_panel.AddF( self._regex_shortcuts, CC.FLAGS_LONE_BUTTON )
|
||||
self._regexes_panel.AddF( self._regex_link, CC.FLAGS_LONE_BUTTON )
|
||||
|
||||
#
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
hbox.AddF( wx.StaticText( self._num_panel, label = '# base/step: ' ), CC.FLAGS_MIXED )
|
||||
hbox.AddF( self._num_base, CC.FLAGS_MIXED )
|
||||
hbox.AddF( self._num_step, CC.FLAGS_MIXED )
|
||||
|
||||
self._num_panel.AddF( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
hbox.AddF( wx.StaticText( self._num_panel, label = '# namespace: ' ), CC.FLAGS_MIXED )
|
||||
hbox.AddF( self._num_namespace, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self._num_panel.AddF( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
second_vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
second_vbox.AddF( self._regexes_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
second_vbox.AddF( self._num_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
self._tags_panel.AddF( self._tags, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._tags_panel.AddF( self._tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self._single_tags_panel.AddF( self._single_tags, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._single_tags_panel.AddF( self._single_tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
hbox.AddF( self._quick_namespaces_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
hbox.AddF( second_vbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
hbox.AddF( self._tags_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
hbox.AddF( self._single_tags_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._paths_list, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( hbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
wx.Panel.__init__( self, parent )
|
||||
|
||||
self._service_key = service_key
|
||||
self._paths = paths
|
||||
|
||||
InitialiseControls()
|
||||
self._load_from_txt_files = False
|
||||
|
||||
PopulateControls()
|
||||
self._paths_list = ClientGUICommon.SaneListCtrl( self, 250, [ ( '#', 50 ), ( 'path', 400 ), ( 'tags', -1 ) ] )
|
||||
|
||||
ArrangeControls()
|
||||
self._paths_list.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
|
||||
self._paths_list.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
|
||||
|
||||
#
|
||||
|
||||
self._quick_namespaces_panel = ClientGUICommon.StaticBox( self, 'quick namespaces' )
|
||||
|
||||
self._quick_namespaces_list = ClientGUICommon.SaneListCtrl( self._quick_namespaces_panel, 200, [ ( 'namespace', 80 ), ( 'regex', -1 ) ], delete_key_callback = self.DeleteQuickNamespaces )
|
||||
|
||||
self._add_quick_namespace_button = wx.Button( self._quick_namespaces_panel, label = 'add' )
|
||||
self._add_quick_namespace_button.Bind( wx.EVT_BUTTON, self.EventAddQuickNamespace )
|
||||
self._add_quick_namespace_button.SetMinSize( ( 20, -1 ) )
|
||||
|
||||
self._edit_quick_namespace_button = wx.Button( self._quick_namespaces_panel, label = 'edit' )
|
||||
self._edit_quick_namespace_button.Bind( wx.EVT_BUTTON, self.EventEditQuickNamespace )
|
||||
self._edit_quick_namespace_button.SetMinSize( ( 20, -1 ) )
|
||||
|
||||
self._delete_quick_namespace_button = wx.Button( self._quick_namespaces_panel, label = 'delete' )
|
||||
self._delete_quick_namespace_button.Bind( wx.EVT_BUTTON, self.EventDeleteQuickNamespace )
|
||||
self._delete_quick_namespace_button.SetMinSize( ( 20, -1 ) )
|
||||
|
||||
#
|
||||
|
||||
self._regexes_panel = ClientGUICommon.StaticBox( self, 'regexes' )
|
||||
|
||||
self._regexes = wx.ListBox( self._regexes_panel )
|
||||
self._regexes.Bind( wx.EVT_LISTBOX_DCLICK, self.EventRemoveRegex )
|
||||
|
||||
self._regex_box = wx.TextCtrl( self._regexes_panel, style=wx.TE_PROCESS_ENTER )
|
||||
self._regex_box.Bind( wx.EVT_TEXT_ENTER, self.EventAddRegex )
|
||||
|
||||
self._regex_shortcuts = ClientGUICommon.RegexButton( self._regexes_panel )
|
||||
|
||||
self._regex_link = wx.HyperlinkCtrl( self._regexes_panel, id = -1, label = 'a good regex introduction', url = 'http://www.aivosto.com/vbtips/regex.html' )
|
||||
|
||||
#
|
||||
|
||||
self._num_panel = ClientGUICommon.StaticBox( self, '#' )
|
||||
|
||||
self._num_base = wx.SpinCtrl( self._num_panel, min = -10000000, max = 10000000, size = ( 60, -1 ) )
|
||||
self._num_base.SetValue( 1 )
|
||||
self._num_base.Bind( wx.EVT_SPINCTRL, self.EventRecalcNum )
|
||||
|
||||
self._num_step = wx.SpinCtrl( self._num_panel, min = -1000000, max = 1000000, size = ( 60, -1 ) )
|
||||
self._num_step.SetValue( 1 )
|
||||
self._num_step.Bind( wx.EVT_SPINCTRL, self.EventRecalcNum )
|
||||
|
||||
self._num_namespace = wx.TextCtrl( self._num_panel, size = ( 100, -1 ) )
|
||||
self._num_namespace.Bind( wx.EVT_TEXT, self.EventNumNamespaceChanged )
|
||||
|
||||
#
|
||||
|
||||
self._tags_panel = ClientGUICommon.StaticBox( self, 'tags for all' )
|
||||
|
||||
self._tags = ClientGUICommon.ListBoxTagsStrings( self._tags_panel, self.TagsRemoved )
|
||||
|
||||
expand_parents = True
|
||||
|
||||
self._tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self._tags_panel, self.EnterTags, expand_parents, CC.LOCAL_FILE_SERVICE_KEY, service_key )
|
||||
|
||||
#
|
||||
|
||||
self._single_tags_panel = ClientGUICommon.StaticBox( self, 'tags just for selected files' )
|
||||
|
||||
self._paths_to_single_tags = collections.defaultdict( set )
|
||||
|
||||
self._single_tags = ClientGUICommon.ListBoxTagsStrings( self._single_tags_panel, self.SingleTagsRemoved )
|
||||
|
||||
expand_parents = True
|
||||
|
||||
self._single_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self._single_tags_panel, self.EnterTagsSingle, expand_parents, CC.LOCAL_FILE_SERVICE_KEY, service_key )
|
||||
|
||||
self._load_from_txt_files_checkbox = wx.CheckBox( self, label = 'try to load tags from neighbouring .txt files' )
|
||||
self._load_from_txt_files_checkbox.SetToolTipString( 'This looks for a [path].txt file, and will try to load line-separated tags from it. Look at thumbnail->share->export->files\'s txt file checkbox for an example.' )
|
||||
self._load_from_txt_files_checkbox.Bind( wx.EVT_CHECKBOX, self.EventLoadFromTextFiles )
|
||||
|
||||
#
|
||||
|
||||
num_base = self._num_base.GetValue()
|
||||
num_step = self._num_step.GetValue()
|
||||
|
||||
for ( num, path ) in enumerate( self._paths ):
|
||||
|
||||
processed_num = num_base + num * num_step
|
||||
|
||||
pretty_num = HydrusData.ConvertIntToPrettyString( processed_num )
|
||||
|
||||
tags = self._GetTags( num, path )
|
||||
|
||||
tags_string = ', '.join( tags )
|
||||
|
||||
self._paths_list.Append( ( pretty_num, path, tags_string ), ( ( num, processed_num ), path, tags ) )
|
||||
|
||||
|
||||
self._single_tag_box.Disable()
|
||||
|
||||
#
|
||||
|
||||
button_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
button_box.AddF( self._add_quick_namespace_button, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
button_box.AddF( self._edit_quick_namespace_button, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
button_box.AddF( self._delete_quick_namespace_button, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self._quick_namespaces_panel.AddF( self._quick_namespaces_list, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._quick_namespaces_panel.AddF( button_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
self._regexes_panel.AddF( self._regexes, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._regexes_panel.AddF( self._regex_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._regexes_panel.AddF( self._regex_shortcuts, CC.FLAGS_LONE_BUTTON )
|
||||
self._regexes_panel.AddF( self._regex_link, CC.FLAGS_LONE_BUTTON )
|
||||
|
||||
#
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
hbox.AddF( wx.StaticText( self._num_panel, label = '# base/step: ' ), CC.FLAGS_MIXED )
|
||||
hbox.AddF( self._num_base, CC.FLAGS_MIXED )
|
||||
hbox.AddF( self._num_step, CC.FLAGS_MIXED )
|
||||
|
||||
self._num_panel.AddF( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
hbox.AddF( wx.StaticText( self._num_panel, label = '# namespace: ' ), CC.FLAGS_MIXED )
|
||||
hbox.AddF( self._num_namespace, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self._num_panel.AddF( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
second_vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
second_vbox.AddF( self._regexes_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
second_vbox.AddF( self._num_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
self._tags_panel.AddF( self._tags, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._tags_panel.AddF( self._tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self._single_tags_panel.AddF( self._single_tags, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._single_tags_panel.AddF( self._single_tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
hbox.AddF( self._quick_namespaces_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
hbox.AddF( second_vbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
hbox.AddF( self._tags_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
hbox.AddF( self._single_tags_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._paths_list, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( self._load_from_txt_files_checkbox, CC.FLAGS_LONE_BUTTON )
|
||||
vbox.AddF( hbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
|
||||
|
@ -3226,6 +3200,30 @@ class DialogPathsToTags( Dialog ):
|
|||
|
||||
tags = []
|
||||
|
||||
if self._load_from_txt_files:
|
||||
|
||||
txt_path = path + '.txt'
|
||||
|
||||
if os.path.exists( txt_path ):
|
||||
|
||||
with open( txt_path, 'rb' ) as f:
|
||||
|
||||
txt_tags_string = f.read()
|
||||
|
||||
|
||||
try:
|
||||
|
||||
txt_tags = [ HydrusData.ToUnicode( tag ) for tag in txt_tags_string.split( os.linesep ) ]
|
||||
|
||||
tags.extend( txt_tags )
|
||||
|
||||
except:
|
||||
|
||||
HydrusData.Print( 'Could not parse the tags from ' + txt_path + '!' )
|
||||
|
||||
|
||||
|
||||
|
||||
tags.extend( self._tags.GetTags() )
|
||||
|
||||
for regex in self._regexes.GetStrings():
|
||||
|
@ -3443,6 +3441,13 @@ class DialogPathsToTags( Dialog ):
|
|||
self._single_tags.SetTags( single_tags )
|
||||
|
||||
|
||||
def EventLoadFromTextFiles( self, event ):
|
||||
|
||||
self._load_from_txt_files = self._load_from_txt_files_checkbox.IsChecked()
|
||||
|
||||
self._RefreshFileList()
|
||||
|
||||
|
||||
def EventNumNamespaceChanged( self, event ): self._RefreshFileList()
|
||||
|
||||
def EventRecalcNum( self, event ):
|
||||
|
|
|
@ -591,17 +591,17 @@ class FullscreenHoverFrameRatings( FullscreenHoverFrame ):
|
|||
self._icon_panel.Hide()
|
||||
|
||||
|
||||
file_repo_strings = self._current_media.GetLocationsManager().GetFileRepositoryStrings()
|
||||
remote_strings = self._current_media.GetLocationsManager().GetRemoteLocationStrings()
|
||||
|
||||
if len( file_repo_strings ) == 0:
|
||||
if len( remote_strings ) == 0:
|
||||
|
||||
self._file_repos.Hide()
|
||||
|
||||
else:
|
||||
|
||||
file_repo_string = os.linesep.join( file_repo_strings )
|
||||
remote_string = os.linesep.join( remote_strings )
|
||||
|
||||
self._file_repos.SetLabel( file_repo_string )
|
||||
self._file_repos.SetLabel( remote_string )
|
||||
|
||||
self._file_repos.Show()
|
||||
|
||||
|
|
|
@ -31,18 +31,18 @@ import HydrusGlobals
|
|||
|
||||
ID_TIMER_ANIMATION = wx.NewId()
|
||||
|
||||
def AddFileServiceKeysToMenu( menu, file_service_keys, phrase, action ):
|
||||
def AddServiceKeysToMenu( menu, service_keys, phrase, action ):
|
||||
|
||||
services_manager = HydrusGlobals.client_controller.GetServicesManager()
|
||||
|
||||
if len( file_service_keys ) == 1:
|
||||
if len( service_keys ) == 1:
|
||||
|
||||
( file_service_key, ) = file_service_keys
|
||||
( service_key, ) = service_keys
|
||||
|
||||
if action == CC.ID_NULL: id = CC.ID_NULL
|
||||
else: id = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( action, file_service_key )
|
||||
else: id = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( action, service_key )
|
||||
|
||||
file_service = services_manager.GetService( file_service_key )
|
||||
file_service = services_manager.GetService( service_key )
|
||||
|
||||
menu.Append( id, phrase + ' ' + file_service.GetName() )
|
||||
|
||||
|
@ -50,12 +50,12 @@ def AddFileServiceKeysToMenu( menu, file_service_keys, phrase, action ):
|
|||
|
||||
submenu = wx.Menu()
|
||||
|
||||
for file_service_key in file_service_keys:
|
||||
for service_key in service_keys:
|
||||
|
||||
if action == CC.ID_NULL: id = CC.ID_NULL
|
||||
else: id = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( action, file_service_key )
|
||||
else: id = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( action, service_key )
|
||||
|
||||
file_service = services_manager.GetService( file_service_key )
|
||||
file_service = services_manager.GetService( service_key )
|
||||
|
||||
submenu.Append( id, file_service.GetName() )
|
||||
|
||||
|
@ -199,6 +199,42 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', path )
|
||||
|
||||
|
||||
def _CopyServiceFilenameToClipboard( self, service_key ):
|
||||
|
||||
display_media = self._focussed_media.GetDisplayMedia()
|
||||
|
||||
hash = display_media.GetHash()
|
||||
|
||||
( filename, ) = HydrusGlobals.client_controller.Read( 'service_filenames', service_key, { hash } )
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', filename )
|
||||
|
||||
|
||||
def _CopyServiceFilenamesToClipboard( self, service_key ):
|
||||
|
||||
hashes = self._GetSelectedHashes( has_location = service_key )
|
||||
|
||||
if len( hashes ) > 0:
|
||||
|
||||
filenames = HydrusGlobals.client_controller.Read( 'service_filenames', service_key, hashes )
|
||||
|
||||
if len( filenames ) > 0:
|
||||
|
||||
copy_string = os.linesep.join( filenames )
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', copy_string )
|
||||
|
||||
else:
|
||||
|
||||
HydrusData.ShowText( 'Could not find any service filenames for that selection!' )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
HydrusData.ShowText( 'Could not find any files with the requested service!' )
|
||||
|
||||
|
||||
|
||||
def _CustomFilter( self, shortcuts_name = None ):
|
||||
|
||||
shortcuts = None
|
||||
|
@ -619,30 +655,49 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
|
||||
|
||||
def _PetitionFiles( self, file_service_key ):
|
||||
def _PetitionFiles( self, remote_service_key ):
|
||||
|
||||
hashes = self._GetSelectedHashes()
|
||||
|
||||
if hashes is not None and len( hashes ) > 0:
|
||||
|
||||
file_service = HydrusGlobals.client_controller.GetServicesManager().GetService( file_service_key )
|
||||
remote_service = HydrusGlobals.client_controller.GetServicesManager().GetService( remote_service_key )
|
||||
|
||||
if len( hashes ) == 1: message = 'Enter a reason for this file to be removed from ' + file_service.GetName() + '.'
|
||||
else: message = 'Enter a reason for these ' + HydrusData.ConvertIntToPrettyString( len( hashes ) ) + ' files to be removed from ' + file_service.GetName() + '.'
|
||||
service_type = remote_service.GetServiceType()
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, message ) as dlg:
|
||||
if service_type == HC.FILE_REPOSITORY:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
if len( hashes ) == 1:
|
||||
|
||||
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_PETITION, ( hashes, dlg.GetValue() ) )
|
||||
message = 'Enter a reason for this file to be removed from ' + remote_service.GetName() + '.'
|
||||
|
||||
service_keys_to_content_updates = { file_service_key : ( content_update, ) }
|
||||
else:
|
||||
|
||||
HydrusGlobals.client_controller.Write( 'content_updates', service_keys_to_content_updates )
|
||||
message = 'Enter a reason for these ' + HydrusData.ConvertIntToPrettyString( len( hashes ) ) + ' files to be removed from ' + remote_service.GetName() + '.'
|
||||
|
||||
|
||||
|
||||
self.SetFocus()
|
||||
with ClientGUIDialogs.DialogTextEntry( self, message ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_PETITION, ( hashes, dlg.GetValue() ) )
|
||||
|
||||
service_keys_to_content_updates = { remote_service_key : ( content_update, ) }
|
||||
|
||||
HydrusGlobals.client_controller.Write( 'content_updates', service_keys_to_content_updates )
|
||||
|
||||
|
||||
|
||||
self.SetFocus()
|
||||
|
||||
elif service_type == HC.IPFS:
|
||||
|
||||
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_PETITION, ( hashes, 'ipfs' ) )
|
||||
|
||||
service_keys_to_content_updates = { remote_service_key : ( content_update, ) }
|
||||
|
||||
HydrusGlobals.client_controller.Write( 'content_updates', service_keys_to_content_updates )
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1717,6 +1772,9 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
elif command == 'copy_hash': self._CopyHashToClipboard( data )
|
||||
elif command == 'copy_hashes': self._CopyHashesToClipboard( data )
|
||||
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
|
||||
elif command == 'copy_hashes': self._CopyHashesToClipboard( data )
|
||||
elif command == 'copy_service_filename': self._CopyServiceFilenameToClipboard( data )
|
||||
elif command == 'copy_service_filenames': self._CopyServiceFilenamesToClipboard( data )
|
||||
elif command == 'copy_path': self._CopyPathToClipboard()
|
||||
elif command == 'ctrl-space':
|
||||
|
||||
|
@ -1959,12 +2017,18 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
multiple_selected = num_selected > 1
|
||||
|
||||
services = HydrusGlobals.client_controller.GetServicesManager().GetServices()
|
||||
services_manager = HydrusGlobals.client_controller.GetServicesManager()
|
||||
|
||||
services = services_manager.GetServices()
|
||||
|
||||
service_keys_to_names = { service.GetServiceKey() : service.GetName() for service in services }
|
||||
|
||||
tag_repositories = [ service for service in services if service.GetServiceType() == HC.TAG_REPOSITORY ]
|
||||
|
||||
file_repositories = [ service for service in services if service.GetServiceType() == HC.FILE_REPOSITORY ]
|
||||
|
||||
ipfs_services = [ service for service in services if service.GetServiceType() == HC.IPFS ]
|
||||
|
||||
local_ratings_services = [ service for service in services if service.GetServiceType() in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) ]
|
||||
|
||||
local_booru_service = [ service for service in services if service.GetServiceType() == HC.LOCAL_BOORU ][0]
|
||||
|
@ -1975,12 +2039,16 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
focussed_is_local = CC.LOCAL_FILE_SERVICE_KEY in self._focussed_media.GetLocationsManager().GetCurrent()
|
||||
|
||||
file_service_keys = { repository.GetServiceKey() for repository in file_repositories }
|
||||
downloadable_file_service_keys = { repository.GetServiceKey() for repository in file_repositories if repository.GetInfo( 'account' ).HasPermission( HC.GET_DATA ) or repository.GetInfo( 'account' ).IsUnknownAccount() }
|
||||
uploadable_file_service_keys = { repository.GetServiceKey() for repository in file_repositories if repository.GetInfo( 'account' ).HasPermission( HC.POST_DATA ) or repository.GetInfo( 'account' ).IsUnknownAccount() }
|
||||
petition_resolvable_file_service_keys = { repository.GetServiceKey() for repository in file_repositories if repository.GetInfo( 'account' ).HasPermission( HC.RESOLVE_PETITIONS ) }
|
||||
petitionable_file_service_keys = { repository.GetServiceKey() for repository in file_repositories if repository.GetInfo( 'account' ).HasPermission( HC.POST_PETITIONS ) } - petition_resolvable_file_service_keys
|
||||
user_manageable_file_service_keys = { repository.GetServiceKey() for repository in file_repositories if repository.GetInfo( 'account' ).HasPermission( HC.MANAGE_USERS ) }
|
||||
admin_file_service_keys = { repository.GetServiceKey() for repository in file_repositories if repository.GetInfo( 'account' ).HasPermission( HC.GENERAL_ADMIN ) }
|
||||
ipfs_service_keys = { service.GetServiceKey() for service in ipfs_services }
|
||||
|
||||
focussed_is_ipfs = True in ( service_key in ipfs_service_keys for service_key in self._focussed_media.GetLocationsManager().GetCurrentRemote() )
|
||||
|
||||
if multiple_selected:
|
||||
|
||||
|
@ -1998,6 +2066,13 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
remote_delete_phrase = 'delete all possible selected from'
|
||||
modify_account_phrase = 'modify the accounts that uploaded selected to'
|
||||
|
||||
pinned_phrase = 'selected pinned to'
|
||||
|
||||
pin_phrase = 'pin all to'
|
||||
rescind_pin_phrase = 'rescind pin to'
|
||||
unpin_phrase = 'unpin all from'
|
||||
rescind_unpin_phrase = 'rescind unpin from'
|
||||
|
||||
manage_tags_phrase = 'selected files\' tags'
|
||||
manage_ratings_phrase = 'selected files\' ratings'
|
||||
|
||||
|
@ -2027,6 +2102,13 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
remote_delete_phrase = 'delete from'
|
||||
modify_account_phrase = 'modify the account that uploaded this to'
|
||||
|
||||
pinned_phrase = 'pinned to'
|
||||
|
||||
pin_phrase = 'pin to'
|
||||
rescind_pin_phrase = 'rescind pin to'
|
||||
unpin_phrase = 'unpin from'
|
||||
rescind_unpin_phrase = 'rescind unpin from'
|
||||
|
||||
manage_tags_phrase = 'file\'s tags'
|
||||
manage_ratings_phrase = 'file\'s ratings'
|
||||
|
||||
|
@ -2045,76 +2127,111 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
def MassUnion( lists ): return { item for item in itertools.chain.from_iterable( lists ) }
|
||||
|
||||
all_current_file_service_keys = [ locations_manager.GetCurrentRemote() for locations_manager in selected_locations_managers ]
|
||||
groups_of_current_remote_service_keys = [ locations_manager.GetCurrentRemote() for locations_manager in selected_locations_managers ]
|
||||
groups_of_pending_remote_service_keys = [ locations_manager.GetPendingRemote() for locations_manager in selected_locations_managers ]
|
||||
groups_of_petitioned_remote_service_keys = [ locations_manager.GetPetitionedRemote() for locations_manager in selected_locations_managers ]
|
||||
groups_of_deleted_remote_service_keys = [ locations_manager.GetDeletedRemote() for locations_manager in selected_locations_managers ]
|
||||
|
||||
current_file_service_keys = HydrusData.IntelligentMassIntersect( all_current_file_service_keys )
|
||||
current_remote_service_keys = MassUnion( groups_of_current_remote_service_keys )
|
||||
pending_remote_service_keys = MassUnion( groups_of_pending_remote_service_keys )
|
||||
petitioned_remote_service_keys = MassUnion( groups_of_petitioned_remote_service_keys )
|
||||
deleted_remote_service_keys = MassUnion( groups_of_deleted_remote_service_keys )
|
||||
|
||||
some_current_file_service_keys = MassUnion( all_current_file_service_keys ) - current_file_service_keys
|
||||
common_current_remote_service_keys = HydrusData.IntelligentMassIntersect( groups_of_current_remote_service_keys )
|
||||
common_pending_remote_service_keys = HydrusData.IntelligentMassIntersect( groups_of_pending_remote_service_keys )
|
||||
common_petitioned_remote_service_keys = HydrusData.IntelligentMassIntersect( groups_of_petitioned_remote_service_keys )
|
||||
common_deleted_remote_service_keys = HydrusData.IntelligentMassIntersect( groups_of_deleted_remote_service_keys )
|
||||
|
||||
all_pending_file_service_keys = [ locations_manager.GetPendingRemote() for locations_manager in selected_locations_managers ]
|
||||
disparate_current_remote_service_keys = current_remote_service_keys - common_current_remote_service_keys
|
||||
disparate_pending_remote_service_keys = pending_remote_service_keys - common_pending_remote_service_keys
|
||||
disparate_petitioned_remote_service_keys = petitioned_remote_service_keys - common_petitioned_remote_service_keys
|
||||
disparate_deleted_remote_service_keys = deleted_remote_service_keys - common_deleted_remote_service_keys
|
||||
|
||||
some_downloading = True in ( CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetPending() for locations_manager in selected_locations_managers )
|
||||
|
||||
pending_file_service_keys = HydrusData.IntelligentMassIntersect( all_pending_file_service_keys )
|
||||
pending_file_service_keys = pending_remote_service_keys.intersection( file_service_keys )
|
||||
petitioned_file_service_keys = petitioned_remote_service_keys.intersection( file_service_keys )
|
||||
|
||||
some_pending_file_service_keys = MassUnion( all_pending_file_service_keys ) - pending_file_service_keys
|
||||
common_current_file_service_keys = common_current_remote_service_keys.intersection( file_service_keys )
|
||||
common_pending_file_service_keys = common_pending_remote_service_keys.intersection( file_service_keys )
|
||||
common_petitioned_file_service_keys = common_petitioned_remote_service_keys.intersection( file_service_keys )
|
||||
common_deleted_file_service_keys = common_deleted_remote_service_keys.intersection( file_service_keys )
|
||||
|
||||
selection_uploaded_file_service_keys = some_pending_file_service_keys.union( pending_file_service_keys )
|
||||
disparate_current_file_service_keys = disparate_current_remote_service_keys.intersection( file_service_keys )
|
||||
disparate_pending_file_service_keys = disparate_pending_remote_service_keys.intersection( file_service_keys )
|
||||
disparate_petitioned_file_service_keys = disparate_petitioned_remote_service_keys.intersection( file_service_keys )
|
||||
disparate_deleted_file_service_keys = disparate_deleted_remote_service_keys.intersection( file_service_keys )
|
||||
|
||||
all_petitioned_file_service_keys = [ locations_manager.GetPetitionedRemote() for locations_manager in selected_locations_managers ]
|
||||
pending_ipfs_service_keys = pending_remote_service_keys.intersection( ipfs_service_keys )
|
||||
petitioned_ipfs_service_keys = petitioned_remote_service_keys.intersection( ipfs_service_keys )
|
||||
|
||||
petitioned_file_service_keys = HydrusData.IntelligentMassIntersect( all_petitioned_file_service_keys )
|
||||
common_current_ipfs_service_keys = common_current_remote_service_keys.intersection( ipfs_service_keys )
|
||||
common_pending_ipfs_service_keys = common_pending_file_service_keys.intersection( ipfs_service_keys )
|
||||
common_petitioned_ipfs_service_keys = common_petitioned_remote_service_keys.intersection( ipfs_service_keys )
|
||||
|
||||
some_petitioned_file_service_keys = MassUnion( all_petitioned_file_service_keys ) - petitioned_file_service_keys
|
||||
|
||||
selection_petitioned_file_service_keys = some_petitioned_file_service_keys.union( petitioned_file_service_keys )
|
||||
|
||||
all_deleted_file_service_keys = [ locations_manager.GetDeletedRemote() for locations_manager in selected_locations_managers ]
|
||||
|
||||
deleted_file_service_keys = HydrusData.IntelligentMassIntersect( all_deleted_file_service_keys )
|
||||
|
||||
some_deleted_file_service_keys = MassUnion( all_deleted_file_service_keys ) - deleted_file_service_keys
|
||||
disparate_current_ipfs_service_keys = disparate_current_remote_service_keys.intersection( ipfs_service_keys )
|
||||
disparate_pending_ipfs_service_keys = disparate_pending_remote_service_keys.intersection( ipfs_service_keys )
|
||||
disparate_petitioned_ipfs_service_keys = disparate_petitioned_remote_service_keys.intersection( ipfs_service_keys )
|
||||
|
||||
# valid commands for the files
|
||||
|
||||
selection_uploadable_file_service_keys = set()
|
||||
uploadable_file_service_keys = set()
|
||||
|
||||
selection_downloadable_file_service_keys = set()
|
||||
downloadable_file_service_keys = set()
|
||||
|
||||
selection_petitionable_file_service_keys = set()
|
||||
petitionable_file_service_keys = set()
|
||||
|
||||
deletable_file_service_keys = set()
|
||||
|
||||
modifyable_file_service_keys = set()
|
||||
|
||||
pinnable_ipfs_service_keys = set()
|
||||
|
||||
unpinnable_ipfs_service_keys = set()
|
||||
|
||||
for locations_manager in selected_locations_managers:
|
||||
|
||||
# FILE REPOS
|
||||
|
||||
# we can upload (set pending) to a repo_id when we have permission, a file is local, not current, not pending, and either ( not deleted or admin )
|
||||
|
||||
if locations_manager.HasLocal(): selection_uploadable_file_service_keys.update( uploadable_file_service_keys - locations_manager.GetCurrentRemote() - locations_manager.GetPendingRemote() - ( locations_manager.GetDeletedRemote() - admin_file_service_keys ) )
|
||||
if locations_manager.HasLocal():
|
||||
|
||||
uploadable_file_service_keys.update( uploadable_file_service_keys - locations_manager.GetCurrentRemote() - locations_manager.GetPendingRemote() - ( locations_manager.GetDeletedRemote() - admin_file_service_keys ) )
|
||||
|
||||
|
||||
# we can download (set pending to local) when we have permission, a file is not local and not already downloading and current
|
||||
|
||||
if not CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent() and not locations_manager.HasDownloading(): selection_downloadable_file_service_keys.update( downloadable_file_service_keys & locations_manager.GetCurrentRemote() )
|
||||
if not CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent() and not locations_manager.HasDownloading():
|
||||
|
||||
downloadable_file_service_keys.update( downloadable_file_service_keys & locations_manager.GetCurrentRemote() )
|
||||
|
||||
|
||||
# we can petition when we have permission and a file is current
|
||||
# we can re-petition an already petitioned file
|
||||
|
||||
selection_petitionable_file_service_keys.update( petitionable_file_service_keys & locations_manager.GetCurrentRemote() )
|
||||
|
||||
|
||||
selection_deletable_file_service_keys = set()
|
||||
|
||||
for locations_manager in selected_locations_managers:
|
||||
petitionable_file_service_keys.update( petitionable_file_service_keys & locations_manager.GetCurrentRemote() )
|
||||
|
||||
# we can delete remote when we have permission and a file is current and it is not already petitioned
|
||||
|
||||
selection_deletable_file_service_keys.update( ( petition_resolvable_file_service_keys & locations_manager.GetCurrentRemote() ) - locations_manager.GetPetitionedRemote() )
|
||||
|
||||
|
||||
selection_modifyable_file_service_keys = set()
|
||||
|
||||
for locations_manager in selected_locations_managers:
|
||||
deletable_file_service_keys.update( ( petition_resolvable_file_service_keys & locations_manager.GetCurrentRemote() ) - locations_manager.GetPetitionedRemote() )
|
||||
|
||||
# we can modify users when we have permission and the file is current or deleted
|
||||
|
||||
selection_modifyable_file_service_keys.update( user_manageable_file_service_keys & ( locations_manager.GetCurrentRemote() | locations_manager.GetDeletedRemote() ) )
|
||||
modifyable_file_service_keys.update( user_manageable_file_service_keys & ( locations_manager.GetCurrentRemote() | locations_manager.GetDeletedRemote() ) )
|
||||
|
||||
# IPFS
|
||||
|
||||
# we can pin if a file is local, not current, not pending
|
||||
|
||||
if locations_manager.HasLocal():
|
||||
|
||||
pinnable_ipfs_service_keys.update( ipfs_service_keys - locations_manager.GetCurrentRemote() - locations_manager.GetPendingRemote() )
|
||||
|
||||
|
||||
# we can unpin a file if it is current and not petitioned
|
||||
|
||||
unpinnable_ipfs_service_keys.update( ( ipfs_service_keys & locations_manager.GetCurrentRemote() ) - locations_manager.GetPetitionedRemote() )
|
||||
|
||||
|
||||
# do the actual menu
|
||||
|
@ -2126,57 +2243,81 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
menu.Append( CC.ID_NULL, thumbnail.GetPrettyAge() )
|
||||
|
||||
|
||||
if len( some_current_file_service_keys ) > 0: AddFileServiceKeysToMenu( menu, some_current_file_service_keys, 'some uploaded to', CC.ID_NULL )
|
||||
if len( disparate_current_file_service_keys ) > 0: AddServiceKeysToMenu( menu, disparate_current_file_service_keys, 'some uploaded to', CC.ID_NULL )
|
||||
|
||||
if len( current_file_service_keys ) > 0: AddFileServiceKeysToMenu( menu, current_file_service_keys, uploaded_phrase, CC.ID_NULL )
|
||||
if len( common_current_file_service_keys ) > 0: AddServiceKeysToMenu( menu, common_current_file_service_keys, uploaded_phrase, CC.ID_NULL )
|
||||
|
||||
if len( some_pending_file_service_keys ) > 0: AddFileServiceKeysToMenu( menu, some_pending_file_service_keys, 'some pending to', CC.ID_NULL )
|
||||
if len( disparate_pending_file_service_keys ) > 0: AddServiceKeysToMenu( menu, disparate_pending_file_service_keys, 'some pending to', CC.ID_NULL )
|
||||
|
||||
if len( pending_file_service_keys ) > 0: AddFileServiceKeysToMenu( menu, pending_file_service_keys, pending_phrase, CC.ID_NULL )
|
||||
if len( common_pending_file_service_keys ) > 0: AddServiceKeysToMenu( menu, common_pending_file_service_keys, pending_phrase, CC.ID_NULL )
|
||||
|
||||
if len( some_petitioned_file_service_keys ) > 0: AddFileServiceKeysToMenu( menu, some_petitioned_file_service_keys, 'some petitioned from', CC.ID_NULL )
|
||||
if len( disparate_petitioned_file_service_keys ) > 0: AddServiceKeysToMenu( menu, disparate_petitioned_file_service_keys, 'some petitioned from', CC.ID_NULL )
|
||||
|
||||
if len( petitioned_file_service_keys ) > 0: AddFileServiceKeysToMenu( menu, petitioned_file_service_keys, petitioned_phrase, CC.ID_NULL )
|
||||
if len( common_petitioned_file_service_keys ) > 0: AddServiceKeysToMenu( menu, common_petitioned_file_service_keys, petitioned_phrase, CC.ID_NULL )
|
||||
|
||||
if len( some_deleted_file_service_keys ) > 0: AddFileServiceKeysToMenu( menu, some_deleted_file_service_keys, 'some deleted from', CC.ID_NULL )
|
||||
if len( disparate_deleted_file_service_keys ) > 0: AddServiceKeysToMenu( menu, disparate_deleted_file_service_keys, 'some deleted from', CC.ID_NULL )
|
||||
|
||||
if len( deleted_file_service_keys ) > 0: AddFileServiceKeysToMenu( menu, deleted_file_service_keys, deleted_phrase, CC.ID_NULL )
|
||||
if len( common_deleted_file_service_keys ) > 0: AddServiceKeysToMenu( menu, common_deleted_file_service_keys, deleted_phrase, CC.ID_NULL )
|
||||
|
||||
if len( disparate_current_ipfs_service_keys ) > 0: AddServiceKeysToMenu( menu, disparate_current_ipfs_service_keys, 'some pinned to', CC.ID_NULL )
|
||||
|
||||
if len( common_current_ipfs_service_keys ) > 0: AddServiceKeysToMenu( menu, common_current_ipfs_service_keys, pinned_phrase, CC.ID_NULL )
|
||||
|
||||
if len( disparate_pending_ipfs_service_keys ) > 0: AddServiceKeysToMenu( menu, disparate_pending_ipfs_service_keys, 'some to be pinned to', CC.ID_NULL )
|
||||
|
||||
if len( common_pending_ipfs_service_keys ) > 0: AddServiceKeysToMenu( menu, common_pending_ipfs_service_keys, pending_phrase, CC.ID_NULL )
|
||||
|
||||
if len( disparate_petitioned_ipfs_service_keys ) > 0: AddServiceKeysToMenu( menu, disparate_petitioned_ipfs_service_keys, 'some to be unpinned from', CC.ID_NULL )
|
||||
|
||||
if len( common_petitioned_ipfs_service_keys ) > 0: AddServiceKeysToMenu( menu, common_petitioned_ipfs_service_keys, unpin_phrase, CC.ID_NULL )
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
||||
#
|
||||
|
||||
len_interesting_file_service_keys = 0
|
||||
len_interesting_remote_service_keys = 0
|
||||
|
||||
len_interesting_file_service_keys += len( selection_downloadable_file_service_keys )
|
||||
len_interesting_file_service_keys += len( selection_uploadable_file_service_keys )
|
||||
len_interesting_file_service_keys += len( selection_uploaded_file_service_keys )
|
||||
len_interesting_file_service_keys += len( selection_petitionable_file_service_keys )
|
||||
len_interesting_file_service_keys += len( selection_petitioned_file_service_keys )
|
||||
len_interesting_file_service_keys += len( selection_deletable_file_service_keys )
|
||||
len_interesting_file_service_keys += len( selection_modifyable_file_service_keys )
|
||||
len_interesting_remote_service_keys += len( downloadable_file_service_keys )
|
||||
len_interesting_remote_service_keys += len( uploadable_file_service_keys )
|
||||
len_interesting_remote_service_keys += len( pending_file_service_keys )
|
||||
len_interesting_remote_service_keys += len( petitionable_file_service_keys )
|
||||
len_interesting_remote_service_keys += len( petitioned_file_service_keys )
|
||||
len_interesting_remote_service_keys += len( deletable_file_service_keys )
|
||||
len_interesting_remote_service_keys += len( modifyable_file_service_keys )
|
||||
len_interesting_remote_service_keys += len( pinnable_ipfs_service_keys )
|
||||
len_interesting_remote_service_keys += len( pending_ipfs_service_keys )
|
||||
len_interesting_remote_service_keys += len( unpinnable_ipfs_service_keys )
|
||||
len_interesting_remote_service_keys += len( petitioned_ipfs_service_keys )
|
||||
|
||||
if len_interesting_file_service_keys > 0:
|
||||
if len_interesting_remote_service_keys > 0:
|
||||
|
||||
file_repo_menu = wx.Menu()
|
||||
remote_action_menu = wx.Menu()
|
||||
|
||||
if len( selection_downloadable_file_service_keys ) > 0: file_repo_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'download' ), download_phrase )
|
||||
if len( downloadable_file_service_keys ) > 0: remote_action_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'download' ), download_phrase )
|
||||
|
||||
if some_downloading: file_repo_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'rescind_download' ), rescind_download_phrase )
|
||||
if some_downloading: remote_action_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'rescind_download' ), rescind_download_phrase )
|
||||
|
||||
if len( selection_uploadable_file_service_keys ) > 0: AddFileServiceKeysToMenu( file_repo_menu, selection_uploadable_file_service_keys, upload_phrase, 'upload' )
|
||||
if len( uploadable_file_service_keys ) > 0: AddServiceKeysToMenu( remote_action_menu, uploadable_file_service_keys, upload_phrase, 'upload' )
|
||||
|
||||
if len( selection_uploaded_file_service_keys ) > 0: AddFileServiceKeysToMenu( file_repo_menu, selection_uploaded_file_service_keys, rescind_upload_phrase, 'rescind_upload' )
|
||||
if len( pending_file_service_keys ) > 0: AddServiceKeysToMenu( remote_action_menu, pending_file_service_keys, rescind_upload_phrase, 'rescind_upload' )
|
||||
|
||||
if len( selection_petitionable_file_service_keys ) > 0: AddFileServiceKeysToMenu( file_repo_menu, selection_petitionable_file_service_keys, petition_phrase, 'petition' )
|
||||
if len( petitionable_file_service_keys ) > 0: AddServiceKeysToMenu( remote_action_menu, petitionable_file_service_keys, petition_phrase, 'petition' )
|
||||
|
||||
if len( selection_petitioned_file_service_keys ) > 0: AddFileServiceKeysToMenu( file_repo_menu, selection_petitioned_file_service_keys, rescind_petition_phrase, 'rescind_petition' )
|
||||
if len( petitioned_file_service_keys ) > 0: AddServiceKeysToMenu( remote_action_menu, petitioned_file_service_keys, rescind_petition_phrase, 'rescind_petition' )
|
||||
|
||||
if len( selection_deletable_file_service_keys ) > 0: AddFileServiceKeysToMenu( file_repo_menu, selection_deletable_file_service_keys, remote_delete_phrase, 'delete' )
|
||||
if len( deletable_file_service_keys ) > 0: AddServiceKeysToMenu( remote_action_menu, deletable_file_service_keys, remote_delete_phrase, 'delete' )
|
||||
|
||||
if len( selection_modifyable_file_service_keys ) > 0: AddFileServiceKeysToMenu( file_repo_menu, selection_modifyable_file_service_keys, modify_account_phrase, 'modify_account' )
|
||||
if len( modifyable_file_service_keys ) > 0: AddServiceKeysToMenu( remote_action_menu, modifyable_file_service_keys, modify_account_phrase, 'modify_account' )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, 'file repositories', file_repo_menu )
|
||||
if len( pinnable_ipfs_service_keys ) > 0: AddServiceKeysToMenu( remote_action_menu, pinnable_ipfs_service_keys, pin_phrase, 'upload' )
|
||||
|
||||
if len( pending_ipfs_service_keys ) > 0: AddServiceKeysToMenu( remote_action_menu, pending_ipfs_service_keys, rescind_pin_phrase, 'rescind_upload' )
|
||||
|
||||
if len( unpinnable_ipfs_service_keys ) > 0: AddServiceKeysToMenu( remote_action_menu, unpinnable_ipfs_service_keys, unpin_phrase, 'petition' )
|
||||
|
||||
if len( petitioned_ipfs_service_keys ) > 0: AddServiceKeysToMenu( remote_action_menu, petitioned_ipfs_service_keys, rescind_unpin_phrase, 'rescind_petition' )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, 'remote services', remote_action_menu )
|
||||
|
||||
|
||||
#
|
||||
|
@ -2301,6 +2442,23 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
if multiple_selected: copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hashes', 'sha256' ) , 'sha256 hashes' )
|
||||
|
||||
|
||||
for ipfs_service_key in self._focussed_media.GetLocationsManager().GetCurrentRemote().intersection( ipfs_service_keys ):
|
||||
|
||||
name = service_keys_to_names[ ipfs_service_key ]
|
||||
|
||||
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_service_filename', ipfs_service_key ) , name + ' multihash' )
|
||||
|
||||
|
||||
if multiple_selected:
|
||||
|
||||
for ipfs_service_key in disparate_current_ipfs_service_keys.union( common_current_ipfs_service_keys ):
|
||||
|
||||
name = service_keys_to_names[ ipfs_service_key ]
|
||||
|
||||
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_service_filenames', ipfs_service_key ) , name + ' multihashes' )
|
||||
|
||||
|
||||
|
||||
if focussed_is_local:
|
||||
|
||||
if self._focussed_media.GetMime() in HC.IMAGES and self._focussed_media.GetDuration() is None:
|
||||
|
@ -2879,33 +3037,70 @@ class Thumbnail( Selectable ):
|
|||
|
||||
# repo icons
|
||||
|
||||
services_manager = HydrusGlobals.client_controller.GetServicesManager()
|
||||
|
||||
repo_icon_x = 0
|
||||
|
||||
num_current = len( locations_manager.GetCurrentRemote() )
|
||||
num_pending = len( locations_manager.GetPendingRemote() )
|
||||
num_petitioned = len( locations_manager.GetPetitionedRemote() )
|
||||
current = locations_manager.GetCurrentRemote()
|
||||
pending = locations_manager.GetPendingRemote()
|
||||
petitioned = locations_manager.GetPetitionedRemote()
|
||||
|
||||
if num_current > num_petitioned:
|
||||
current_to_display = current.difference( petitioned )
|
||||
|
||||
#
|
||||
|
||||
service_types = [ services_manager.GetService( service_key ).GetServiceType() for service_key in current_to_display ]
|
||||
|
||||
if HC.FILE_REPOSITORY in service_types:
|
||||
|
||||
dc.DrawBitmap( CC.GlobalBMPs.file_repository, repo_icon_x, 0 )
|
||||
|
||||
repo_icon_x += 20
|
||||
|
||||
|
||||
if num_pending > 0:
|
||||
if HC.IPFS in service_types:
|
||||
|
||||
dc.DrawBitmap( CC.GlobalBMPs.ipfs, repo_icon_x, 0 )
|
||||
|
||||
repo_icon_x += 20
|
||||
|
||||
|
||||
#
|
||||
|
||||
service_types = [ services_manager.GetService( service_key ).GetServiceType() for service_key in pending ]
|
||||
|
||||
if HC.FILE_REPOSITORY in service_types:
|
||||
|
||||
dc.DrawBitmap( CC.GlobalBMPs.file_repository_pending, repo_icon_x, 0 )
|
||||
|
||||
repo_icon_x += 20
|
||||
|
||||
|
||||
if num_petitioned > 0:
|
||||
if HC.IPFS in service_types:
|
||||
|
||||
dc.DrawBitmap( CC.GlobalBMPs.ipfs_pending, repo_icon_x, 0 )
|
||||
|
||||
repo_icon_x += 20
|
||||
|
||||
|
||||
#
|
||||
|
||||
service_types = [ services_manager.GetService( service_key ).GetServiceType() for service_key in petitioned ]
|
||||
|
||||
if HC.FILE_REPOSITORY in service_types:
|
||||
|
||||
dc.DrawBitmap( CC.GlobalBMPs.file_repository_petitioned, repo_icon_x, 0 )
|
||||
|
||||
repo_icon_x += 20
|
||||
|
||||
|
||||
if HC.IPFS in service_types:
|
||||
|
||||
dc.DrawBitmap( CC.GlobalBMPs.ipfs_petitioned, repo_icon_x, 0 )
|
||||
|
||||
repo_icon_x += 20
|
||||
|
||||
|
||||
return bmp
|
||||
|
||||
|
||||
|
|
|
@ -92,46 +92,6 @@ class LocationsManager( object ):
|
|||
return self._deleted - self.LOCAL_LOCATIONS
|
||||
|
||||
|
||||
def GetFileRepositoryStrings( self ):
|
||||
|
||||
current = self.GetCurrentRemote()
|
||||
pending = self.GetPendingRemote()
|
||||
petitioned = self.GetPetitionedRemote()
|
||||
|
||||
file_repo_services = HydrusGlobals.client_controller.GetServicesManager().GetServices( ( HC.FILE_REPOSITORY, ) )
|
||||
|
||||
file_repo_services = list( file_repo_services )
|
||||
|
||||
cmp_func = lambda a, b: cmp( a.GetName(), b.GetName() )
|
||||
|
||||
file_repo_services.sort( cmp = cmp_func )
|
||||
|
||||
file_repo_service_keys_and_names = [ ( file_repo_service.GetServiceKey(), file_repo_service.GetName() ) for file_repo_service in file_repo_services ]
|
||||
|
||||
file_repo_strings = []
|
||||
|
||||
for ( service_key, name ) in file_repo_service_keys_and_names:
|
||||
|
||||
if service_key in pending:
|
||||
|
||||
file_repo_strings.append( name + ' (+)' )
|
||||
|
||||
elif service_key in current:
|
||||
|
||||
if service_key in petitioned:
|
||||
|
||||
file_repo_strings.append( name + ' (-)' )
|
||||
|
||||
else:
|
||||
|
||||
file_repo_strings.append( name )
|
||||
|
||||
|
||||
|
||||
|
||||
return file_repo_strings
|
||||
|
||||
|
||||
def GetPending( self ): return self._pending
|
||||
def GetPendingRemote( self ):
|
||||
|
||||
|
@ -144,6 +104,50 @@ class LocationsManager( object ):
|
|||
return self._petitioned - self.LOCAL_LOCATIONS
|
||||
|
||||
|
||||
def GetRemoteLocationStrings( self ):
|
||||
|
||||
current = self.GetCurrentRemote()
|
||||
pending = self.GetPendingRemote()
|
||||
petitioned = self.GetPetitionedRemote()
|
||||
|
||||
remote_services = HydrusGlobals.client_controller.GetServicesManager().GetServices( ( HC.FILE_REPOSITORY, HC.IPFS ) )
|
||||
|
||||
remote_services = list( remote_services )
|
||||
|
||||
def key( s ):
|
||||
|
||||
return s.GetName()
|
||||
|
||||
|
||||
remote_services.sort( key = key )
|
||||
|
||||
remote_service_strings = []
|
||||
|
||||
for remote_service in remote_services:
|
||||
|
||||
name = remote_service.GetName()
|
||||
service_key = remote_service.GetServiceKey()
|
||||
|
||||
if service_key in pending:
|
||||
|
||||
remote_service_strings.append( name + ' (+)' )
|
||||
|
||||
elif service_key in current:
|
||||
|
||||
if service_key in petitioned:
|
||||
|
||||
remote_service_strings.append( name + ' (-)' )
|
||||
|
||||
else:
|
||||
|
||||
remote_service_strings.append( name )
|
||||
|
||||
|
||||
|
||||
|
||||
return remote_service_strings
|
||||
|
||||
|
||||
def HasDownloading( self ): return CC.LOCAL_FILE_SERVICE_KEY in self._pending
|
||||
|
||||
def HasLocal( self ): return len( self._current.intersection( self.LOCAL_LOCATIONS ) ) > 0
|
||||
|
@ -1241,7 +1245,7 @@ class MediaResult( object ):
|
|||
service_type = service.GetServiceType()
|
||||
|
||||
if service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ): tags_manager.ProcessContentUpdate( service_key, content_update )
|
||||
elif service_type in ( HC.FILE_REPOSITORY, HC.LOCAL_FILE ):
|
||||
elif service_type in ( HC.FILE_REPOSITORY, HC.LOCAL_FILE, HC.IPFS ):
|
||||
|
||||
if service_type == HC.LOCAL_FILE:
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import HydrusSerialisable
|
|||
import errno
|
||||
import httplib
|
||||
import os
|
||||
import requests
|
||||
import socket
|
||||
import socks
|
||||
import threading
|
||||
|
@ -109,6 +110,73 @@ def ConvertHydrusGETArgsToQuery( request_args ):
|
|||
|
||||
return query
|
||||
|
||||
def RequestsGet( url, stream = False ):
|
||||
|
||||
response = requests.get( url, stream = stream )
|
||||
|
||||
RequestsCheckResponse( response )
|
||||
|
||||
return response
|
||||
|
||||
def RequestsPost( url, data = None, files = None ):
|
||||
|
||||
response = requests.post( url, data = data, files = files )
|
||||
|
||||
RequestsCheckResponse( response )
|
||||
|
||||
return response
|
||||
|
||||
def RequestsCheckResponse( response ):
|
||||
|
||||
if not response.ok:
|
||||
|
||||
error_text = response.content
|
||||
|
||||
if len( error_text ) > 1024:
|
||||
|
||||
large_chunk = error_text[:4096]
|
||||
|
||||
smaller_chunk = large_chunk[:256]
|
||||
|
||||
HydrusData.DebugPrint( large_chunk )
|
||||
|
||||
error_text = 'The server\'s error text was too long to display. The first part follows, while a larger chunk has been written to the log.'
|
||||
error_text += os.linesep
|
||||
error_text += smaller_chunk
|
||||
|
||||
|
||||
if response.status_code == 304:
|
||||
|
||||
eclass = HydrusExceptions.NotModifiedException
|
||||
|
||||
elif response.status_code == 401:
|
||||
|
||||
eclass = HydrusExceptions.PermissionException
|
||||
|
||||
elif response.status_code == 403:
|
||||
|
||||
eclass = HydrusExceptions.ForbiddenException
|
||||
|
||||
elif response.status_code == 404:
|
||||
|
||||
eclass = HydrusExceptions.NotFoundException
|
||||
|
||||
elif response.status_code == 419:
|
||||
|
||||
eclass = HydrusExceptions.SessionException
|
||||
|
||||
elif response.status_code == 426:
|
||||
|
||||
eclass = HydrusExceptions.NetworkVersionException
|
||||
|
||||
else:
|
||||
|
||||
eclass = HydrusExceptions.NetworkException
|
||||
|
||||
|
||||
raise eclass( error_text )
|
||||
|
||||
|
||||
def ParseURL( url ):
|
||||
|
||||
try:
|
||||
|
|
|
@ -53,7 +53,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 17
|
||||
SOFTWARE_VERSION = 193
|
||||
SOFTWARE_VERSION = 194
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
@ -275,6 +275,8 @@ UNDETERMINED_WM = 19
|
|||
VIDEO_MKV = 20
|
||||
VIDEO_WEBM = 21
|
||||
APPLICATION_JSON = 22
|
||||
VIDEO_APNG = 23
|
||||
UNDETERMINED_PNG = 24
|
||||
APPLICATION_OCTET_STREAM = 100
|
||||
APPLICATION_UNKNOWN = 101
|
||||
|
||||
|
@ -325,6 +327,7 @@ mime_enum_lookup[ 'audio/ogg' ] = AUDIO_OGG
|
|||
mime_enum_lookup[ 'audio/flac' ] = AUDIO_FLAC
|
||||
mime_enum_lookup[ 'audio/x-ms-wma' ] = AUDIO_WMA
|
||||
mime_enum_lookup[ 'text/html' ] = TEXT_HTML
|
||||
mime_enum_lookup[ 'video/png' ] = VIDEO_APNG
|
||||
mime_enum_lookup[ 'video/x-flv' ] = VIDEO_FLV
|
||||
mime_enum_lookup[ 'video/mp4' ] = VIDEO_MP4
|
||||
mime_enum_lookup[ 'video/x-ms-wmv' ] = VIDEO_WMV
|
||||
|
@ -356,6 +359,7 @@ mime_string_lookup[ AUDIO_FLAC ] = 'audio/flac'
|
|||
mime_string_lookup[ AUDIO_WMA ] = 'audio/x-ms-wma'
|
||||
mime_string_lookup[ AUDIO ] = 'audio'
|
||||
mime_string_lookup[ TEXT_HTML ] = 'text/html'
|
||||
mime_string_lookup[ VIDEO_APNG ] = 'video/png'
|
||||
mime_string_lookup[ VIDEO_FLV ] = 'video/x-flv'
|
||||
mime_string_lookup[ VIDEO_MP4 ] = 'video/mp4'
|
||||
mime_string_lookup[ VIDEO_WMV ] = 'video/x-ms-wmv'
|
||||
|
@ -385,6 +389,7 @@ mime_ext_lookup[ AUDIO_OGG ] = '.ogg'
|
|||
mime_ext_lookup[ AUDIO_FLAC ] = '.flac'
|
||||
mime_ext_lookup[ AUDIO_WMA ] = '.wma'
|
||||
mime_ext_lookup[ TEXT_HTML ] = '.html'
|
||||
mime_ext_lookup[ VIDEO_APNG ] = '.png'
|
||||
mime_ext_lookup[ VIDEO_FLV ] = '.flv'
|
||||
mime_ext_lookup[ VIDEO_MP4 ] = '.mp4'
|
||||
mime_ext_lookup[ VIDEO_WMV ] = '.wmv'
|
||||
|
|
|
@ -27,7 +27,7 @@ header_and_mime = [
|
|||
( 0, '\xff\xd8', HC.IMAGE_JPEG ),
|
||||
( 0, 'GIF87a', HC.IMAGE_GIF ),
|
||||
( 0, 'GIF89a', HC.IMAGE_GIF ),
|
||||
( 0, '\x89PNG', HC.IMAGE_PNG ),
|
||||
( 0, '\x89PNG', HC.UNDETERMINED_PNG ),
|
||||
( 0, 'BM', HC.IMAGE_BMP ),
|
||||
( 0, 'CWS', HC.APPLICATION_FLASH ),
|
||||
( 0, 'FWS', HC.APPLICATION_FLASH ),
|
||||
|
@ -216,7 +216,25 @@ def GetMime( path ):
|
|||
|
||||
# we'll catch and verify wma later
|
||||
|
||||
else: return mime
|
||||
elif mime == HC.UNDETERMINED_PNG:
|
||||
|
||||
return HC.IMAGE_PNG
|
||||
|
||||
# atm (Feb 2016), ffmpeg doesn't report duration for apngs, so can't do this just yet.
|
||||
#
|
||||
#if HydrusVideoHandling.HasVideoStream( path ):
|
||||
#
|
||||
# return HC.VIDEO_APNG
|
||||
#
|
||||
#else:
|
||||
#
|
||||
# return HC.IMAGE_PNG
|
||||
#
|
||||
|
||||
else:
|
||||
|
||||
return mime
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -72,15 +72,13 @@ def GetLocalIP(): return socket.gethostbyname( socket.gethostname() )
|
|||
def AddUPnPMapping( internal_client, internal_port, external_port, protocol, description, duration = 3600 ):
|
||||
|
||||
cmd = [ upnpc_path, '-e', description, '-a', internal_client, str( internal_port ), str( external_port ), protocol, str( duration ) ]
|
||||
HydrusData.DebugPrint( cmd )
|
||||
|
||||
p = subprocess.Popen( cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, startupinfo = HydrusData.GetSubprocessStartupInfo() )
|
||||
|
||||
p.wait()
|
||||
|
||||
( output, error ) = p.communicate()
|
||||
|
||||
HydrusData.DebugPrint( output )
|
||||
|
||||
if output is not None and 'failed with code' in output:
|
||||
|
||||
raise Exception( 'Problem while trying to add UPnP mapping:' + os.linesep * 2 + HydrusData.ToUnicode( output ) )
|
||||
|
|
|
@ -88,7 +88,14 @@ def GetMatroskaOrWebMProperties( path ):
|
|||
|
||||
def HasVideoStream( path ):
|
||||
|
||||
info = Hydrusffmpeg_parse_infos( path )
|
||||
try:
|
||||
|
||||
info = Hydrusffmpeg_parse_infos( path )
|
||||
|
||||
except IOError as e:
|
||||
HydrusData.ShowException( e )
|
||||
return False
|
||||
|
||||
|
||||
return info[ 'video_found' ]
|
||||
|
||||
|
|
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 637 B |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 616 B |
After Width: | Height: | Size: 623 B |