3287 lines
124 KiB
Python
3287 lines
124 KiB
Python
import collections
|
|
import collections.abc
|
|
import json
|
|
import os
|
|
import threading
|
|
import time
|
|
import traceback
|
|
import typing
|
|
|
|
CBOR_AVAILABLE = False
|
|
|
|
try:
|
|
|
|
import cbor2
|
|
CBOR_AVAILABLE = True
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
from twisted.web.static import File as FileResource
|
|
|
|
from hydrus.core import HydrusConstants as HC
|
|
from hydrus.core import HydrusData
|
|
from hydrus.core import HydrusExceptions
|
|
from hydrus.core import HydrusFileHandling
|
|
from hydrus.core import HydrusImageHandling
|
|
from hydrus.core import HydrusGlobals as HG
|
|
from hydrus.core import HydrusPaths
|
|
from hydrus.core import HydrusTags
|
|
from hydrus.core import HydrusTemp
|
|
from hydrus.core.networking import HydrusNetworkVariableHandling
|
|
from hydrus.core.networking import HydrusServerRequest
|
|
from hydrus.core.networking import HydrusServerResources
|
|
|
|
from hydrus.client import ClientAPI
|
|
from hydrus.client import ClientConstants as CC
|
|
from hydrus.client import ClientLocation
|
|
from hydrus.client import ClientSearch
|
|
from hydrus.client import ClientSearchParseSystemPredicates
|
|
from hydrus.client import ClientThreading
|
|
from hydrus.client.importing import ClientImportFiles
|
|
from hydrus.client.importing.options import FileImportOptions
|
|
from hydrus.client.media import ClientMedia
|
|
from hydrus.client.metadata import ClientTags
|
|
from hydrus.client.networking import ClientNetworkingContexts
|
|
from hydrus.client.networking import ClientNetworkingFunctions
|
|
|
|
local_booru_css = FileResource( os.path.join( HC.STATIC_DIR, 'local_booru_style.css' ), defaultType = 'text/css' )
|
|
|
|
LOCAL_BOORU_INT_PARAMS = set()
|
|
LOCAL_BOORU_BYTE_PARAMS = { 'share_key', 'hash' }
|
|
LOCAL_BOORU_STRING_PARAMS = set()
|
|
LOCAL_BOORU_JSON_PARAMS = set()
|
|
LOCAL_BOORU_JSON_BYTE_LIST_PARAMS = set()
|
|
|
|
CLIENT_API_INT_PARAMS = { 'file_id', 'file_sort_type' }
|
|
CLIENT_API_BYTE_PARAMS = { 'hash', 'destination_page_key', 'page_key', 'Hydrus-Client-API-Access-Key', 'Hydrus-Client-API-Session-Key', 'tag_service_key', 'file_service_key' }
|
|
CLIENT_API_STRING_PARAMS = { 'name', 'url', 'domain', 'search', 'file_service_name', 'tag_service_name', 'reason', 'tag_display_type', 'source_hash_type', 'desired_hash_type' }
|
|
CLIENT_API_JSON_PARAMS = { 'basic_permissions', 'system_inbox', 'system_archive', 'tags', 'file_ids', 'only_return_identifiers', 'only_return_basic_information', 'create_new_file_ids', 'detailed_url_information', 'hide_service_names_tags', 'hide_service_keys_tags', 'simple', 'file_sort_asc', 'return_hashes', 'return_file_ids', 'include_notes', 'notes', 'note_names', 'doublecheck_file_system' }
|
|
CLIENT_API_JSON_BYTE_LIST_PARAMS = { 'hashes' }
|
|
CLIENT_API_JSON_BYTE_DICT_PARAMS = { 'service_keys_to_tags', 'service_keys_to_actions_to_tags', 'service_keys_to_additional_tags' }
|
|
|
|
def Dumps( data, mime ):
|
|
|
|
if mime == HC.APPLICATION_CBOR:
|
|
|
|
if not CBOR_AVAILABLE:
|
|
|
|
raise HydrusExceptions.NotAcceptable( 'Sorry, this service does not support CBOR!' )
|
|
|
|
|
|
return cbor2.dumps( data )
|
|
|
|
else:
|
|
|
|
return json.dumps( data )
|
|
|
|
|
|
def CheckHashLength( hashes, hash_type = 'sha256' ):
|
|
|
|
if len( hashes ) == 0:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Sorry, I was expecting at least 1 {} hash, but none were given!'.format( hash_type ) )
|
|
|
|
|
|
hash_types_to_length = {
|
|
'sha256' : 32,
|
|
'md5' : 16,
|
|
'sha1' : 20,
|
|
'sha512' : 64
|
|
}
|
|
|
|
hash_length = hash_types_to_length[ hash_type ]
|
|
|
|
for hash in hashes:
|
|
|
|
if len( hash ) != hash_length:
|
|
|
|
raise HydrusExceptions.BadRequestException(
|
|
'Sorry, one of the given hashes was the wrong length! {} hashes should be {} bytes long, but {} is {} bytes long!'.format(
|
|
hash_type,
|
|
hash_length,
|
|
hash.hex(),
|
|
len( hash )
|
|
)
|
|
)
|
|
|
|
|
|
|
|
def ConvertServiceNamesDictToKeys( allowed_service_types, service_name_dict ):
|
|
|
|
service_key_dict = {}
|
|
|
|
for ( service_name, value ) in service_name_dict.items():
|
|
|
|
try:
|
|
|
|
service_key = HG.client_controller.services_manager.GetServiceKeyFromName( allowed_service_types, service_name )
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Could not find the service "{}", or it was the wrong type!'.format( service_name ) )
|
|
|
|
|
|
service_key_dict[ service_key ] = value
|
|
|
|
|
|
return service_key_dict
|
|
|
|
def ParseLocalBooruGETArgs( requests_args ):
|
|
|
|
args = HydrusNetworkVariableHandling.ParseTwistedRequestGETArgs( requests_args, LOCAL_BOORU_INT_PARAMS, LOCAL_BOORU_BYTE_PARAMS, LOCAL_BOORU_STRING_PARAMS, LOCAL_BOORU_JSON_PARAMS, LOCAL_BOORU_JSON_BYTE_LIST_PARAMS )
|
|
|
|
return args
|
|
|
|
def ParseClientAPIGETArgs( requests_args ):
|
|
|
|
args = HydrusNetworkVariableHandling.ParseTwistedRequestGETArgs( requests_args, CLIENT_API_INT_PARAMS, CLIENT_API_BYTE_PARAMS, CLIENT_API_STRING_PARAMS, CLIENT_API_JSON_PARAMS, CLIENT_API_JSON_BYTE_LIST_PARAMS )
|
|
|
|
return args
|
|
|
|
def ParseClientAPIPOSTByteArgs( args ):
|
|
|
|
if not isinstance( args, dict ):
|
|
|
|
raise HydrusExceptions.BadRequestException( 'The given parameter did not seem to be a JSON Object!' )
|
|
|
|
|
|
parsed_request_args = HydrusNetworkVariableHandling.ParsedRequestArguments( args )
|
|
|
|
for var_name in CLIENT_API_BYTE_PARAMS:
|
|
|
|
if var_name in parsed_request_args:
|
|
|
|
try:
|
|
|
|
raw_value = parsed_request_args[ var_name ]
|
|
|
|
# In JSON, if someone puts 'null' for an optional value, treat that as 'did not enter anything'
|
|
if raw_value is None:
|
|
|
|
del parsed_request_args[ var_name ]
|
|
|
|
continue
|
|
|
|
|
|
v = bytes.fromhex( raw_value )
|
|
|
|
if len( v ) == 0:
|
|
|
|
del parsed_request_args[ var_name ]
|
|
|
|
else:
|
|
|
|
parsed_request_args[ var_name ] = v
|
|
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'I was expecting to parse \'{}\' as a hex string, but it failed.'.format( var_name ) )
|
|
|
|
|
|
|
|
|
|
for var_name in CLIENT_API_JSON_BYTE_LIST_PARAMS:
|
|
|
|
if var_name in parsed_request_args:
|
|
|
|
try:
|
|
|
|
raw_value = parsed_request_args[ var_name ]
|
|
|
|
# In JSON, if someone puts 'null' for an optional value, treat that as 'did not enter anything'
|
|
if raw_value is None:
|
|
|
|
del parsed_request_args[ var_name ]
|
|
|
|
continue
|
|
|
|
|
|
v_list = [ bytes.fromhex( hash_hex ) for hash_hex in raw_value ]
|
|
|
|
v_list = [ v for v in v_list if len( v ) > 0 ]
|
|
|
|
if len( v_list ) == 0:
|
|
|
|
del parsed_request_args[ var_name ]
|
|
|
|
else:
|
|
|
|
parsed_request_args[ var_name ] = v_list
|
|
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'I was expecting to parse \'{}\' as a list of hex strings, but it failed.'.format( var_name ) )
|
|
|
|
|
|
|
|
|
|
for var_name in CLIENT_API_JSON_BYTE_DICT_PARAMS:
|
|
|
|
if var_name in parsed_request_args:
|
|
|
|
try:
|
|
|
|
raw_dict = parsed_request_args[ var_name ]
|
|
|
|
# In JSON, if someone puts 'null' for an optional value, treat that as 'did not enter anything'
|
|
if raw_dict is None:
|
|
|
|
del parsed_request_args[ var_name ]
|
|
|
|
continue
|
|
|
|
|
|
bytes_dict = {}
|
|
|
|
for ( key, value ) in raw_dict.items():
|
|
|
|
if len( key ) == 0:
|
|
|
|
continue
|
|
|
|
|
|
bytes_key = bytes.fromhex( key )
|
|
|
|
bytes_dict[ bytes_key ] = value
|
|
|
|
|
|
if len( bytes_dict ) == 0:
|
|
|
|
del parsed_request_args[ var_name ]
|
|
|
|
else:
|
|
|
|
parsed_request_args[ var_name ] = bytes_dict
|
|
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'I was expecting to parse \'{}\' as a dictionary of hex strings to other data, but it failed.'.format( var_name ) )
|
|
|
|
|
|
|
|
|
|
return parsed_request_args
|
|
|
|
def ParseClientAPIPOSTArgs( request ):
|
|
|
|
request.content.seek( 0 )
|
|
|
|
if not request.requestHeaders.hasHeader( 'Content-Type' ):
|
|
|
|
request_content_type_mime = HC.APPLICATION_JSON
|
|
|
|
parsed_request_args = HydrusNetworkVariableHandling.ParsedRequestArguments()
|
|
|
|
total_bytes_read = 0
|
|
|
|
else:
|
|
|
|
content_types = request.requestHeaders.getRawHeaders( 'Content-Type' )
|
|
|
|
content_type = content_types[0]
|
|
|
|
if ';' in content_type:
|
|
|
|
# lmao: application/json;charset=utf-8
|
|
content_type = content_type.split( ';', 1 )[0]
|
|
|
|
|
|
try:
|
|
|
|
request_content_type_mime = HC.mime_enum_lookup[ content_type ]
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Did not recognise Content-Type header!' )
|
|
|
|
|
|
total_bytes_read = 0
|
|
|
|
if request_content_type_mime == HC.APPLICATION_JSON:
|
|
|
|
json_bytes = request.content.read()
|
|
|
|
total_bytes_read += len( json_bytes )
|
|
|
|
json_string = str( json_bytes, 'utf-8' )
|
|
|
|
try:
|
|
|
|
args = json.loads( json_string )
|
|
|
|
except json.decoder.JSONDecodeError as e:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Sorry, did not understand the JSON you gave me: {}'.format( str( e ) ) )
|
|
|
|
|
|
parsed_request_args = ParseClientAPIPOSTByteArgs( args )
|
|
|
|
|
|
elif request_content_type_mime == HC.APPLICATION_CBOR:
|
|
|
|
if not CBOR_AVAILABLE:
|
|
|
|
raise HydrusExceptions.NotAcceptable( 'Sorry, this service does not support CBOR!' )
|
|
|
|
|
|
cbor_bytes = request.content.read()
|
|
|
|
total_bytes_read += len( cbor_bytes )
|
|
|
|
args = cbor2.loads( cbor_bytes )
|
|
|
|
parsed_request_args = ParseClientAPIPOSTByteArgs( args )
|
|
|
|
else:
|
|
|
|
parsed_request_args = HydrusNetworkVariableHandling.ParsedRequestArguments()
|
|
|
|
( os_file_handle, temp_path ) = HydrusTemp.GetTempPath()
|
|
|
|
request.temp_file_info = ( os_file_handle, temp_path )
|
|
|
|
with open( temp_path, 'wb' ) as f:
|
|
|
|
for block in HydrusPaths.ReadFileLikeAsBlocks( request.content ):
|
|
|
|
f.write( block )
|
|
|
|
total_bytes_read += len( block )
|
|
|
|
|
|
|
|
|
|
|
|
return ( parsed_request_args, total_bytes_read )
|
|
|
|
def ParseClientAPISearchPredicates( request ) -> typing.List[ ClientSearch.Predicate ]:
|
|
|
|
default_search_values = {}
|
|
|
|
default_search_values[ 'tags' ] = []
|
|
default_search_values[ 'system_inbox' ] = False
|
|
default_search_values[ 'system_archive' ] = False
|
|
|
|
for ( key, value ) in default_search_values.items():
|
|
|
|
if key not in request.parsed_request_args:
|
|
|
|
request.parsed_request_args[ key ] = value
|
|
|
|
|
|
|
|
system_inbox = request.parsed_request_args[ 'system_inbox' ]
|
|
system_archive = request.parsed_request_args[ 'system_archive' ]
|
|
|
|
tags = request.parsed_request_args[ 'tags' ]
|
|
|
|
predicates = ConvertTagListToPredicates( request, tags )
|
|
|
|
if system_inbox:
|
|
|
|
predicates.append( ClientSearch.Predicate( predicate_type = ClientSearch.PREDICATE_TYPE_SYSTEM_INBOX ) )
|
|
|
|
elif system_archive:
|
|
|
|
predicates.append( ClientSearch.Predicate( predicate_type = ClientSearch.PREDICATE_TYPE_SYSTEM_ARCHIVE ) )
|
|
|
|
|
|
if len( predicates ) == 0:
|
|
|
|
return predicates
|
|
|
|
|
|
we_have_at_least_one_inclusive_tag = True in ( predicate.GetType() == ClientSearch.PREDICATE_TYPE_TAG and predicate.IsInclusive() for predicate in predicates )
|
|
|
|
if not we_have_at_least_one_inclusive_tag:
|
|
|
|
try:
|
|
|
|
request.client_api_permissions.CheckCanSeeAllFiles()
|
|
|
|
except HydrusExceptions.InsufficientCredentialsException:
|
|
|
|
raise HydrusExceptions.InsufficientCredentialsException( 'Sorry, you do not have permission to see all files on this client. Please add a regular tag to your search.' )
|
|
|
|
|
|
|
|
return predicates
|
|
|
|
def ParseLocationContext( request: HydrusServerRequest.HydrusRequest, default: ClientLocation.LocationContext ):
|
|
|
|
if 'file_service_key' in request.parsed_request_args or 'file_service_name' in request.parsed_request_args:
|
|
|
|
if 'file_service_key' in request.parsed_request_args:
|
|
|
|
file_service_key = request.parsed_request_args[ 'file_service_key' ]
|
|
|
|
else:
|
|
|
|
file_service_name = request.parsed_request_args[ 'file_service_name' ]
|
|
|
|
try:
|
|
|
|
file_service_key = HG.client_controller.services_manager.GetServiceKeyFromName( HC.ALL_FILE_SERVICES, file_service_name )
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Could not find the service "{}"!'.format( file_service_name ) )
|
|
|
|
|
|
|
|
try:
|
|
|
|
service_type = HG.client_controller.services_manager.GetServiceType( file_service_key )
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Could not find that file service!' )
|
|
|
|
|
|
if service_type not in HC.ALL_FILE_SERVICES:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Sorry, that service key did not give a file service!' )
|
|
|
|
|
|
return ClientLocation.LocationContext.STATICCreateSimple( file_service_key )
|
|
|
|
else:
|
|
|
|
return default
|
|
|
|
|
|
def ParseHashes( request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
hashes = set()
|
|
|
|
if 'hash' in request.parsed_request_args:
|
|
|
|
hash = request.parsed_request_args.GetValue( 'hash', bytes )
|
|
|
|
hashes.add( hash )
|
|
|
|
|
|
if 'hashes' in request.parsed_request_args:
|
|
|
|
more_hashes = request.parsed_request_args.GetValue( 'hashes', list, expected_list_type = bytes )
|
|
|
|
hashes.update( more_hashes )
|
|
|
|
|
|
if 'file_id' in request.parsed_request_args:
|
|
|
|
hash_id = request.parsed_request_args.GetValue( 'file_id', int )
|
|
|
|
hash_ids_to_hashes = HG.client_controller.Read( 'hash_ids_to_hashes', hash_ids = [ hash_id ] )
|
|
|
|
hashes.update( hash_ids_to_hashes.values() )
|
|
|
|
|
|
if 'file_ids' in request.parsed_request_args:
|
|
|
|
hash_ids = request.parsed_request_args.GetValue( 'file_ids', list, expected_list_type = int )
|
|
|
|
hash_ids_to_hashes = HG.client_controller.Read( 'hash_ids_to_hashes', hash_ids = hash_ids )
|
|
|
|
hashes.update( hash_ids_to_hashes.values() )
|
|
|
|
|
|
CheckHashLength( hashes )
|
|
|
|
return hashes
|
|
|
|
def ParseRequestedResponseMime( request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
# let them ask for something specifically, else default to what they asked in, finally default to json
|
|
|
|
if request.requestHeaders.hasHeader( 'Accept' ):
|
|
|
|
accepts = request.requestHeaders.getRawHeaders( 'Accept' )
|
|
|
|
accept = accepts[0]
|
|
|
|
if 'cbor' in accept and 'json' not in accept:
|
|
|
|
return HC.APPLICATION_CBOR
|
|
|
|
elif 'json' in accept and 'cbor' not in accept:
|
|
|
|
return HC.APPLICATION_JSON
|
|
|
|
|
|
|
|
if request.requestHeaders.hasHeader( 'Content-Type' ):
|
|
|
|
content_types = request.requestHeaders.getRawHeaders( 'Content-Type' )
|
|
|
|
content_type = content_types[0]
|
|
|
|
if 'cbor' in content_type:
|
|
|
|
return HC.APPLICATION_CBOR
|
|
|
|
elif 'json' in content_type:
|
|
|
|
return HC.APPLICATION_JSON
|
|
|
|
|
|
|
|
|
|
if b'cbor' in request.args:
|
|
|
|
return HC.APPLICATION_CBOR
|
|
|
|
|
|
return HC.APPLICATION_JSON
|
|
|
|
|
|
def ConvertTagListToPredicates( request, tag_list, do_permission_check = True, error_on_invalid_tag = True ) -> typing.List[ ClientSearch.Predicate ]:
|
|
|
|
or_tag_lists = [ tag for tag in tag_list if isinstance( tag, list ) ]
|
|
tag_strings = [ tag for tag in tag_list if isinstance( tag, str ) ]
|
|
|
|
system_predicate_strings = [ tag for tag in tag_strings if tag.startswith( 'system:' ) ]
|
|
tags = [ tag for tag in tag_strings if not tag.startswith( 'system:' ) ]
|
|
|
|
negated_tags = [ tag for tag in tags if tag.startswith( '-' ) ]
|
|
tags = [ tag for tag in tags if not tag.startswith( '-' ) ]
|
|
|
|
dirty_negated_tags = negated_tags
|
|
dirty_tags = tags
|
|
|
|
negated_tags = HydrusTags.CleanTags( dirty_negated_tags )
|
|
tags = HydrusTags.CleanTags( dirty_tags )
|
|
|
|
if error_on_invalid_tag:
|
|
|
|
jobs = [
|
|
( dirty_negated_tags, negated_tags ),
|
|
( dirty_tags, tags )
|
|
]
|
|
|
|
for ( dirty_ts, ts ) in jobs:
|
|
|
|
if len( ts ) != dirty_ts:
|
|
|
|
for dirty_t in dirty_ts:
|
|
|
|
try:
|
|
|
|
clean_t = HydrusTags.CleanTag( dirty_t )
|
|
|
|
HydrusTags.CheckTagNotEmpty( clean_t )
|
|
|
|
except Exception as e:
|
|
|
|
message = 'Could not understand the tag: "{}"'.format( dirty_t )
|
|
|
|
raise HydrusExceptions.BadRequestException( message )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if do_permission_check:
|
|
|
|
raw_inclusive_tags = [ tag for tag in tags if '*' not in tags ]
|
|
|
|
if len( raw_inclusive_tags ) == 0:
|
|
|
|
if len( negated_tags ) > 0:
|
|
|
|
try:
|
|
|
|
request.client_api_permissions.CheckCanSeeAllFiles()
|
|
|
|
except HydrusExceptions.InsufficientCredentialsException:
|
|
|
|
raise HydrusExceptions.InsufficientCredentialsException( 'Sorry, if you want to search negated tags without regular tags, you need permission to search everything!' )
|
|
|
|
|
|
|
|
if len( system_predicate_strings ) > 0:
|
|
|
|
try:
|
|
|
|
request.client_api_permissions.CheckCanSeeAllFiles()
|
|
|
|
except HydrusExceptions.InsufficientCredentialsException:
|
|
|
|
raise HydrusExceptions.InsufficientCredentialsException( 'Sorry, if you want to search system predicates without regular tags, you need permission to search everything!' )
|
|
|
|
|
|
|
|
if len( or_tag_lists ) > 0:
|
|
|
|
try:
|
|
|
|
request.client_api_permissions.CheckCanSeeAllFiles()
|
|
|
|
except HydrusExceptions.InsufficientCredentialsException:
|
|
|
|
raise HydrusExceptions.InsufficientCredentialsException( 'Sorry, if you want to search OR predicates without regular tags, you need permission to search everything!' )
|
|
|
|
|
|
|
|
else:
|
|
|
|
# check positive tags, not negative!
|
|
request.client_api_permissions.CheckCanSearchTags( tags )
|
|
|
|
|
|
|
|
predicates = []
|
|
|
|
for or_tag_list in or_tag_lists:
|
|
|
|
or_preds = ConvertTagListToPredicates( request, or_tag_list, do_permission_check = False )
|
|
|
|
predicates.append( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_OR_CONTAINER, or_preds ) )
|
|
|
|
|
|
predicates.extend( ClientSearchParseSystemPredicates.ParseSystemPredicateStringsToPredicates( system_predicate_strings ) )
|
|
|
|
search_tags = [ ( True, tag ) for tag in tags ]
|
|
search_tags.extend( ( ( False, tag ) for tag in negated_tags ) )
|
|
|
|
for ( inclusive, tag ) in search_tags:
|
|
|
|
( namespace, subtag ) = HydrusTags.SplitTag( tag )
|
|
|
|
if '*' in tag:
|
|
|
|
if subtag == '*':
|
|
|
|
tag = namespace
|
|
predicate_type = ClientSearch.PREDICATE_TYPE_NAMESPACE
|
|
|
|
else:
|
|
|
|
predicate_type = ClientSearch.PREDICATE_TYPE_WILDCARD
|
|
|
|
|
|
else:
|
|
|
|
predicate_type = ClientSearch.PREDICATE_TYPE_TAG
|
|
|
|
|
|
predicates.append( ClientSearch.Predicate( predicate_type = predicate_type, value = tag, inclusive = inclusive ) )
|
|
|
|
|
|
return predicates
|
|
|
|
class HydrusResourceBooru( HydrusServerResources.HydrusResource ):
|
|
|
|
def _callbackParseGETArgs( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
parsed_request_args = ParseLocalBooruGETArgs( request.args )
|
|
|
|
request.parsed_request_args = parsed_request_args
|
|
|
|
return request
|
|
|
|
|
|
def _callbackParsePOSTArgs( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
return request
|
|
|
|
|
|
def _reportDataUsed( self, request, num_bytes ):
|
|
|
|
self._service.ReportDataUsed( num_bytes )
|
|
|
|
|
|
def _reportRequestUsed( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
self._service.ReportRequestUsed()
|
|
|
|
|
|
def _checkService( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
HydrusServerResources.HydrusResource._checkService( self, request )
|
|
|
|
if not self._service.BandwidthOK():
|
|
|
|
raise HydrusExceptions.BandwidthException( 'This service has run out of bandwidth. Please try again later.' )
|
|
|
|
|
|
|
|
class HydrusResourceBooruFile( HydrusResourceBooru ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
share_key = request.parsed_request_args[ 'share_key' ]
|
|
hash = request.parsed_request_args[ 'hash' ]
|
|
|
|
HG.client_controller.local_booru_manager.CheckFileAuthorised( share_key, hash )
|
|
|
|
media_result = HG.client_controller.local_booru_manager.GetMediaResult( share_key, hash )
|
|
|
|
try:
|
|
|
|
mime = media_result.GetMime()
|
|
|
|
path = HG.client_controller.client_files_manager.GetFilePath( hash, mime )
|
|
|
|
except HydrusExceptions.FileMissingException:
|
|
|
|
raise HydrusExceptions.NotFoundException( 'Could not find that file!' )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = mime, path = path )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceBooruGallery( HydrusResourceBooru ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
# in future, make this a standard frame with a search key that'll load xml or yaml AJAX stuff
|
|
# with file info included, so the page can sort and whatever
|
|
|
|
share_key = request.parsed_request_args.GetValue( 'share_key', bytes )
|
|
|
|
local_booru_manager = HG.client_controller.local_booru_manager
|
|
|
|
local_booru_manager.CheckShareAuthorised( share_key )
|
|
|
|
( name, text, timeout, media_results ) = local_booru_manager.GetGalleryInfo( share_key )
|
|
|
|
body = '''<html>
|
|
<head>'''
|
|
|
|
if name == '': body += '''
|
|
<title>hydrus network local booru share</title>'''
|
|
else: body += '''
|
|
<title>''' + name + '''</title>'''
|
|
|
|
body += '''
|
|
|
|
<link href="hydrus.ico" rel="shortcut icon" />
|
|
<link href="style.css" rel="stylesheet" type="text/css" />'''
|
|
|
|
( thumbnail_width, thumbnail_height ) = HC.options[ 'thumbnail_dimensions' ]
|
|
|
|
body += '''
|
|
<style>
|
|
.thumbnail_container { width: ''' + str( thumbnail_width ) + '''px; height: ''' + str( thumbnail_height ) + '''px; }
|
|
</style>'''
|
|
|
|
body += '''
|
|
</head>
|
|
<body>'''
|
|
|
|
body += '''
|
|
<div class="timeout">This share ''' + HydrusData.ConvertTimestampToPrettyExpires( timeout ) + '''.</div>'''
|
|
|
|
if name != '': body += '''
|
|
<h3>''' + name + '''</h3>'''
|
|
|
|
if text != '':
|
|
|
|
newline = '''</p>
|
|
<p>'''
|
|
|
|
body += '''
|
|
<p>''' + text.replace( os.linesep, newline ).replace( '\n', newline ) + '''</p>'''
|
|
|
|
body+= '''
|
|
<div class="media">'''
|
|
|
|
for media_result in media_results:
|
|
|
|
hash = media_result.GetHash()
|
|
mime = media_result.GetMime()
|
|
|
|
# if mime in flash or pdf or whatever, get other thumbnail
|
|
|
|
body += '''
|
|
<span class="thumbnail">
|
|
<span class="thumbnail_container">
|
|
<a href="page?share_key=''' + share_key.hex() + '''&hash=''' + hash.hex() + '''">
|
|
<img src="thumbnail?share_key=''' + share_key.hex() + '''&hash=''' + hash.hex() + '''" />
|
|
</a>
|
|
</span>
|
|
</span>'''
|
|
|
|
|
|
body += '''
|
|
</div>
|
|
<div class="footer"><a href="https://hydrusnetwork.github.io/hydrus/">hydrus network</a></div>
|
|
</body>
|
|
</html>'''
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = HC.TEXT_HTML, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceBooruPage( HydrusResourceBooru ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
share_key = request.parsed_request_args.GetValue( 'share_key', bytes )
|
|
hash = request.parsed_request_args.GetValue( 'hash', bytes )
|
|
|
|
local_booru_manager = HG.client_controller.local_booru_manager
|
|
|
|
local_booru_manager.CheckFileAuthorised( share_key, hash )
|
|
|
|
( name, text, timeout, media_result ) = local_booru_manager.GetPageInfo( share_key, hash )
|
|
|
|
body = '''<html>
|
|
<head>'''
|
|
|
|
if name == '': body += '''
|
|
<title>hydrus network local booru share</title>'''
|
|
else: body += '''
|
|
<title>''' + name + '''</title>'''
|
|
|
|
body += '''
|
|
|
|
<link href="hydrus.ico" rel="shortcut icon" />
|
|
<link href="style.css" rel="stylesheet" type="text/css" />'''
|
|
|
|
body += '''
|
|
</head>
|
|
<body>'''
|
|
|
|
body += '''
|
|
<div class="timeout">This share ''' + HydrusData.ConvertTimestampToPrettyExpires( timeout ) + '''.</div>'''
|
|
|
|
if name != '': body += '''
|
|
<h3>''' + name + '''</h3>'''
|
|
|
|
if text != '':
|
|
|
|
newline = '''</p>
|
|
<p>'''
|
|
|
|
body += '''
|
|
<p>''' + text.replace( os.linesep, newline ).replace( '\n', newline ) + '''</p>'''
|
|
|
|
body+= '''
|
|
<div class="media">'''
|
|
|
|
mime = media_result.GetMime()
|
|
|
|
if mime in HC.IMAGES or mime in HC.ANIMATIONS:
|
|
|
|
( width, height ) = media_result.GetResolution()
|
|
|
|
body += '''
|
|
<img width="''' + str( width ) + '''" height="''' + str( height ) + '''" src="file?share_key=''' + share_key.hex() + '''&hash=''' + hash.hex() + '''" />'''
|
|
|
|
elif mime in HC.VIDEO:
|
|
|
|
( width, height ) = media_result.GetResolution()
|
|
|
|
body += '''
|
|
<video width="''' + str( width ) + '''" height="''' + str( height ) + '''" controls="" loop="" autoplay="" src="file?share_key=''' + share_key.hex() + '''&hash=''' + hash.hex() + '''" />
|
|
<p><a href="file?share_key=''' + share_key.hex() + '''&hash=''' + hash.hex() + '''">link to ''' + HC.mime_string_lookup[ mime ] + ''' file</a></p>'''
|
|
|
|
elif mime == HC.APPLICATION_FLASH:
|
|
|
|
( width, height ) = media_result.GetResolution()
|
|
|
|
body += '''
|
|
<embed width="''' + str( width ) + '''" height="''' + str( height ) + '''" src="file?share_key=''' + share_key.hex() + '''&hash=''' + hash.hex() + '''" />
|
|
<p><a href="file?share_key=''' + share_key.hex() + '''&hash=''' + hash.hex() + '''">link to ''' + HC.mime_string_lookup[ mime ] + ''' file</a></p>'''
|
|
|
|
else:
|
|
|
|
body += '''
|
|
<p><a href="file?share_key=''' + share_key.hex() + '''&hash=''' + hash.hex() + '''">link to ''' + HC.mime_string_lookup[ mime ] + ''' file</a></p>'''
|
|
|
|
|
|
body += '''
|
|
</div>
|
|
<div class="footer"><a href="https://hydrusnetwork.github.io/hydrus/">hydrus network</a></div>
|
|
</body>
|
|
</html>'''
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = HC.TEXT_HTML, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceBooruThumbnail( HydrusResourceBooru ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
share_key = request.parsed_request_args.GetValue( 'share_key', bytes )
|
|
hash = request.parsed_request_args.GetValue( 'hash', bytes )
|
|
|
|
local_booru_manager = HG.client_controller.local_booru_manager
|
|
|
|
local_booru_manager.CheckFileAuthorised( share_key, hash )
|
|
|
|
media_result = local_booru_manager.GetMediaResult( share_key, hash )
|
|
|
|
mime = media_result.GetMime()
|
|
|
|
response_context_mime = HC.IMAGE_PNG
|
|
|
|
if mime in HC.MIMES_WITH_THUMBNAILS:
|
|
|
|
client_files_manager = HG.client_controller.client_files_manager
|
|
|
|
path = client_files_manager.GetThumbnailPath( media_result )
|
|
|
|
response_context_mime = HC.APPLICATION_UNKNOWN
|
|
|
|
if not os.path.exists( path ):
|
|
|
|
path = HydrusPaths.mimes_to_default_thumbnail_paths[ mime ]
|
|
|
|
|
|
else:
|
|
|
|
path = HydrusPaths.mimes_to_default_thumbnail_paths[ mime ]
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = response_context_mime, path = path )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPI( HydrusServerResources.HydrusResource ):
|
|
|
|
BLOCKED_WHEN_BUSY = True
|
|
|
|
def _callbackParseGETArgs( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
parsed_request_args = ParseClientAPIGETArgs( request.args )
|
|
|
|
request.parsed_request_args = parsed_request_args
|
|
|
|
requested_response_mime = ParseRequestedResponseMime( request )
|
|
|
|
if requested_response_mime == HC.APPLICATION_CBOR and not CBOR_AVAILABLE:
|
|
|
|
raise HydrusExceptions.NotAcceptable( 'Sorry, this service does not support CBOR!' )
|
|
|
|
|
|
request.preferred_mime = requested_response_mime
|
|
|
|
return request
|
|
|
|
|
|
def _callbackParsePOSTArgs( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
( parsed_request_args, total_bytes_read ) = ParseClientAPIPOSTArgs( request )
|
|
|
|
self._reportDataUsed( request, total_bytes_read )
|
|
|
|
request.parsed_request_args = parsed_request_args
|
|
|
|
requested_response_mime = ParseRequestedResponseMime( request )
|
|
|
|
if requested_response_mime == HC.APPLICATION_CBOR and not CBOR_AVAILABLE:
|
|
|
|
raise HydrusExceptions.NotAcceptable( 'Sorry, this service does not support CBOR!' )
|
|
|
|
|
|
request.preferred_mime = requested_response_mime
|
|
|
|
return request
|
|
|
|
|
|
def _reportDataUsed( self, request, num_bytes ):
|
|
|
|
self._service.ReportDataUsed( num_bytes )
|
|
|
|
|
|
def _reportRequestUsed( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
self._service.ReportRequestUsed()
|
|
|
|
HG.client_controller.ResetIdleTimerFromClientAPI()
|
|
|
|
|
|
def _checkService( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
HydrusServerResources.HydrusResource._checkService( self, request )
|
|
|
|
if self.BLOCKED_WHEN_BUSY and HG.client_busy.locked():
|
|
|
|
raise HydrusExceptions.ServerBusyException( 'This server is busy, please try again later.' )
|
|
|
|
|
|
if not self._service.BandwidthOK():
|
|
|
|
raise HydrusExceptions.BandwidthException( 'This service has run out of bandwidth. Please try again later.' )
|
|
|
|
|
|
|
|
class HydrusResourceClientAPIPermissionsRequest( HydrusResourceClientAPI ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
if not ClientAPI.api_request_dialog_open:
|
|
|
|
raise HydrusExceptions.ConflictException( 'The permission registration dialog is not open. Please open it under "review services" in the hydrus client.' )
|
|
|
|
|
|
name = request.parsed_request_args.GetValue( 'name', str )
|
|
|
|
basic_permissions = request.parsed_request_args.GetValue( 'basic_permissions', list, expected_list_type = int )
|
|
|
|
basic_permissions = [ int( value ) for value in basic_permissions ]
|
|
|
|
api_permissions = ClientAPI.APIPermissions( name = name, basic_permissions = basic_permissions )
|
|
|
|
ClientAPI.last_api_permissions_request = api_permissions
|
|
|
|
access_key = api_permissions.GetAccessKey()
|
|
|
|
body_dict = {}
|
|
|
|
body_dict[ 'access_key' ] = access_key.hex()
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIVersion( HydrusResourceClientAPI ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
body_dict = {}
|
|
|
|
body_dict[ 'version' ] = HC.CLIENT_API_VERSION
|
|
body_dict[ 'hydrus_version' ] = HC.SOFTWARE_VERSION
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestricted( HydrusResourceClientAPI ):
|
|
|
|
def _callbackCheckAccountRestrictions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
HydrusResourceClientAPI._callbackCheckAccountRestrictions( self, request )
|
|
|
|
self._CheckAPIPermissions( request )
|
|
|
|
return request
|
|
|
|
|
|
def _callbackEstablishAccountFromHeader( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
access_key = self._ParseClientAPIAccessKey( request, 'header' )
|
|
|
|
if access_key is not None:
|
|
|
|
self._EstablishAPIPermissions( request, access_key )
|
|
|
|
|
|
return request
|
|
|
|
|
|
def _callbackEstablishAccountFromArgs( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
if request.client_api_permissions is None:
|
|
|
|
access_key = self._ParseClientAPIAccessKey( request, 'args' )
|
|
|
|
if access_key is not None:
|
|
|
|
self._EstablishAPIPermissions( request, access_key )
|
|
|
|
|
|
|
|
if request.client_api_permissions is None:
|
|
|
|
raise HydrusExceptions.MissingCredentialsException( 'No access key or session key provided!' )
|
|
|
|
|
|
return request
|
|
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
def _EstablishAPIPermissions( self, request, access_key ):
|
|
|
|
try:
|
|
|
|
api_permissions = HG.client_controller.client_api_manager.GetPermissions( access_key )
|
|
|
|
except HydrusExceptions.DataMissing as e:
|
|
|
|
raise HydrusExceptions.InsufficientCredentialsException( str( e ) )
|
|
|
|
|
|
request.client_api_permissions = api_permissions
|
|
|
|
|
|
def _ParseClientAPIKey( self, request, source, name_of_key ):
|
|
|
|
key = None
|
|
|
|
if source == 'header':
|
|
|
|
if request.requestHeaders.hasHeader( name_of_key ):
|
|
|
|
key_texts = request.requestHeaders.getRawHeaders( name_of_key )
|
|
|
|
key_text = key_texts[0]
|
|
|
|
try:
|
|
|
|
key = bytes.fromhex( key_text )
|
|
|
|
except:
|
|
|
|
raise Exception( 'Problem parsing {}!'.format( name_of_key ) )
|
|
|
|
|
|
|
|
elif source == 'args':
|
|
|
|
if name_of_key in request.parsed_request_args:
|
|
|
|
key = request.parsed_request_args.GetValue( name_of_key, bytes )
|
|
|
|
|
|
|
|
return key
|
|
|
|
|
|
def _ParseClientAPIAccessKey( self, request, source ):
|
|
|
|
access_key = self._ParseClientAPIKey( request, source, 'Hydrus-Client-API-Access-Key' )
|
|
|
|
if access_key is None:
|
|
|
|
session_key = self._ParseClientAPIKey( request, source, 'Hydrus-Client-API-Session-Key' )
|
|
|
|
if session_key is None:
|
|
|
|
return None
|
|
|
|
|
|
try:
|
|
|
|
access_key = HG.client_controller.client_api_manager.GetAccessKey( session_key )
|
|
|
|
except HydrusExceptions.DataMissing as e:
|
|
|
|
raise HydrusExceptions.SessionException( str( e ) )
|
|
|
|
|
|
|
|
return access_key
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAccount( HydrusResourceClientAPIRestricted ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
pass
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAccountSessionKey( HydrusResourceClientAPIRestrictedAccount ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
new_session_key = HG.client_controller.client_api_manager.GenerateSessionKey( request.client_api_permissions.GetAccessKey() )
|
|
|
|
body_dict = {}
|
|
|
|
body_dict[ 'session_key' ] = new_session_key.hex()
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAccountVerify( HydrusResourceClientAPIRestrictedAccount ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
api_permissions = request.client_api_permissions
|
|
|
|
basic_permissions = api_permissions.GetBasicPermissions()
|
|
human_description = api_permissions.ToHumanString()
|
|
|
|
body_dict = {}
|
|
|
|
body_dict[ 'basic_permissions' ] = list( basic_permissions ) # set->list for json
|
|
body_dict[ 'human_description' ] = human_description
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedGetServices( HydrusResourceClientAPIRestricted ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
request.client_api_permissions.CheckAtLeastOnePermission(
|
|
(
|
|
ClientAPI.CLIENT_API_PERMISSION_ADD_FILES,
|
|
ClientAPI.CLIENT_API_PERMISSION_ADD_TAGS,
|
|
ClientAPI.CLIENT_API_PERMISSION_ADD_NOTES,
|
|
ClientAPI.CLIENT_API_PERMISSION_MANAGE_PAGES,
|
|
ClientAPI.CLIENT_API_PERMISSION_SEARCH_FILES
|
|
)
|
|
)
|
|
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
jobs = [
|
|
( ( HC.LOCAL_TAG, ), 'local_tags' ),
|
|
( ( HC.TAG_REPOSITORY, ), 'tag_repositories' ),
|
|
( ( HC.LOCAL_FILE_DOMAIN, ), 'local_files' ),
|
|
( ( HC.LOCAL_FILE_UPDATE_DOMAIN, ), 'local_updates' ),
|
|
( ( HC.FILE_REPOSITORY, ), 'file_repositories' ),
|
|
( ( HC.COMBINED_LOCAL_FILE, ), 'all_local_files' ),
|
|
( ( HC.COMBINED_LOCAL_MEDIA, ), 'all_local_media' ),
|
|
( ( HC.COMBINED_FILE, ), 'all_known_files' ),
|
|
( ( HC.COMBINED_TAG, ), 'all_known_tags' ),
|
|
( ( HC.LOCAL_FILE_TRASH_DOMAIN, ), 'trash' )
|
|
]
|
|
|
|
body_dict = {}
|
|
|
|
for ( service_types, name ) in jobs:
|
|
|
|
services = HG.client_controller.services_manager.GetServices( service_types )
|
|
|
|
services_list = []
|
|
|
|
for service in services:
|
|
|
|
service_dict = {
|
|
'name' : service.GetName(),
|
|
'type' : service.GetServiceType(),
|
|
'type_pretty' : HC.service_string_lookup[ service.GetServiceType() ],
|
|
'service_key' : service.GetServiceKey().hex()
|
|
}
|
|
|
|
services_list.append( service_dict )
|
|
|
|
|
|
body_dict[ name ] = services_list
|
|
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddFiles( HydrusResourceClientAPIRestricted ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_ADD_FILES )
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddFilesAddFile( HydrusResourceClientAPIRestrictedAddFiles ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
if not hasattr( request, 'temp_file_info' ):
|
|
|
|
path = request.parsed_request_args.GetValue( 'path', str )
|
|
|
|
if not os.path.exists( path ):
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Path "{}" does not exist!'.format( path ) )
|
|
|
|
|
|
( os_file_handle, temp_path ) = HydrusTemp.GetTempPath()
|
|
|
|
request.temp_file_info = ( os_file_handle, temp_path )
|
|
|
|
HydrusPaths.MirrorFile( path, temp_path )
|
|
|
|
|
|
( os_file_handle, temp_path ) = request.temp_file_info
|
|
|
|
file_import_options = HG.client_controller.new_options.GetDefaultFileImportOptions( FileImportOptions.IMPORT_TYPE_QUIET )
|
|
|
|
file_import_job = ClientImportFiles.FileImportJob( temp_path, file_import_options )
|
|
|
|
try:
|
|
|
|
file_import_status = file_import_job.DoWork()
|
|
|
|
except:
|
|
|
|
file_import_status = ClientImportFiles.FileImportStatus( CC.STATUS_ERROR, file_import_job.GetHash(), note = traceback.format_exc() )
|
|
|
|
|
|
body_dict = {}
|
|
|
|
body_dict[ 'status' ] = file_import_status.status
|
|
body_dict[ 'hash' ] = HydrusData.BytesToNoneOrHex( file_import_status.hash )
|
|
body_dict[ 'note' ] = file_import_status.note
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddFilesArchiveFiles( HydrusResourceClientAPIRestrictedAddFiles ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
hashes = ParseHashes( request )
|
|
|
|
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_ARCHIVE, hashes )
|
|
|
|
service_keys_to_content_updates = { CC.COMBINED_LOCAL_FILE_SERVICE_KEY : [ content_update ] }
|
|
|
|
if len( service_keys_to_content_updates ) > 0:
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddFilesDeleteFiles( HydrusResourceClientAPIRestrictedAddFiles ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
location_context = ParseLocationContext( request, ClientLocation.LocationContext.STATICCreateSimple( CC.COMBINED_LOCAL_MEDIA_SERVICE_KEY ) )
|
|
|
|
if 'reason' in request.parsed_request_args:
|
|
|
|
reason = request.parsed_request_args.GetValue( 'reason', str )
|
|
|
|
else:
|
|
|
|
reason = 'Deleted via Client API.'
|
|
|
|
|
|
hashes = ParseHashes( request )
|
|
|
|
# expand this to take reason
|
|
|
|
location_context.LimitToServiceTypes( HG.client_controller.services_manager.GetServiceType, ( HC.COMBINED_LOCAL_FILE, HC.COMBINED_LOCAL_MEDIA, HC.LOCAL_FILE_DOMAIN ) )
|
|
|
|
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, hashes, reason = reason )
|
|
|
|
for service_key in location_context.current_service_keys:
|
|
|
|
service_keys_to_content_updates = { service_key : [ content_update ] }
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddFilesUnarchiveFiles( HydrusResourceClientAPIRestrictedAddFiles ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
hashes = ParseHashes( request )
|
|
|
|
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_INBOX, hashes )
|
|
|
|
service_keys_to_content_updates = { CC.COMBINED_LOCAL_FILE_SERVICE_KEY : [ content_update ] }
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddFilesUndeleteFiles( HydrusResourceClientAPIRestrictedAddFiles ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
location_context = ParseLocationContext( request, ClientLocation.LocationContext.STATICCreateSimple( CC.COMBINED_LOCAL_MEDIA_SERVICE_KEY ) )
|
|
|
|
hashes = ParseHashes( request )
|
|
|
|
location_context.LimitToServiceTypes( HG.client_controller.services_manager.GetServiceType, ( HC.LOCAL_FILE_DOMAIN, HC.COMBINED_LOCAL_MEDIA ) )
|
|
|
|
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_UNDELETE, hashes )
|
|
|
|
for service_key in location_context.current_service_keys:
|
|
|
|
service_keys_to_content_updates = { service_key : [ content_update ] }
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddNotes( HydrusResourceClientAPIRestricted ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_ADD_NOTES )
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddNotesSetNotes( HydrusResourceClientAPIRestrictedAddNotes ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
if 'hash' in request.parsed_request_args:
|
|
|
|
hash = request.parsed_request_args.GetValue( 'hash', bytes )
|
|
|
|
elif 'file_id' in request.parsed_request_args:
|
|
|
|
hash_id = request.parsed_request_args.GetValue( 'file_id', int )
|
|
|
|
hash_ids_to_hashes = HG.client_controller.Read( 'hash_ids_to_hashes', hash_ids = [ hash_id ] )
|
|
|
|
hash = hash_ids_to_hashes[ hash_id ]
|
|
|
|
else:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'There was no file identifier or hash given!' )
|
|
|
|
|
|
new_names_to_notes = request.parsed_request_args.GetValue( 'notes', dict, expected_dict_types = ( str, str ) )
|
|
|
|
merge_cleverly = request.parsed_request_args.GetValue( 'merge_cleverly', bool, default_value = False )
|
|
|
|
if merge_cleverly:
|
|
|
|
from hydrus.client.importing.options import NoteImportOptions
|
|
|
|
extend_existing_note_if_possible = request.parsed_request_args.GetValue( 'extend_existing_note_if_possible', bool, default_value = True )
|
|
conflict_resolution = request.parsed_request_args.GetValue( 'conflict_resolution', int, default_value = NoteImportOptions.NOTE_IMPORT_CONFLICT_RENAME )
|
|
|
|
if conflict_resolution not in NoteImportOptions.note_import_conflict_str_lookup:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'The given conflict resolution type was not in the allowed range!' )
|
|
|
|
|
|
note_import_options = NoteImportOptions.NoteImportOptions()
|
|
|
|
note_import_options.SetIsDefault( False )
|
|
note_import_options.SetExtendExistingNoteIfPossible( extend_existing_note_if_possible )
|
|
note_import_options.SetConflictResolution( conflict_resolution )
|
|
|
|
media_result = HG.client_controller.Read( 'media_result', hash )
|
|
|
|
existing_names_to_notes = media_result.GetNotesManager().GetNamesToNotes()
|
|
|
|
new_names_to_notes = note_import_options.GetUpdateeNamesToNotes( existing_names_to_notes, new_names_to_notes )
|
|
|
|
|
|
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_NOTES, HC.CONTENT_UPDATE_SET, ( hash, name, note ) ) for ( name, note ) in new_names_to_notes.items() ]
|
|
|
|
if len( content_updates ) > 0:
|
|
|
|
service_keys_to_content_updates = { CC.LOCAL_NOTES_SERVICE_KEY : content_updates }
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
|
|
|
|
|
|
body_dict = {}
|
|
|
|
body_dict[ 'notes' ] = new_names_to_notes
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddNotesDeleteNotes( HydrusResourceClientAPIRestrictedAddNotes ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
if 'hash' in request.parsed_request_args:
|
|
|
|
hash = request.parsed_request_args.GetValue( 'hash', bytes )
|
|
|
|
elif 'file_id' in request.parsed_request_args:
|
|
|
|
hash_id = request.parsed_request_args.GetValue( 'file_id', int )
|
|
|
|
hash_ids_to_hashes = HG.client_controller.Read( 'hash_ids_to_hashes', hash_ids = [ hash_id ] )
|
|
|
|
hash = hash_ids_to_hashes[ hash_id ]
|
|
|
|
else:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'There was no file identifier or hash given!' )
|
|
|
|
note_names = request.parsed_request_args.GetValue( 'note_names', list, expected_list_type = str )
|
|
|
|
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_NOTES, HC.CONTENT_UPDATE_DELETE, ( hash, name ) ) for name in note_names ]
|
|
|
|
service_keys_to_content_updates = { CC.LOCAL_NOTES_SERVICE_KEY : content_updates }
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddTags( HydrusResourceClientAPIRestricted ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_ADD_TAGS )
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddTagsAddTags( HydrusResourceClientAPIRestrictedAddTags ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
hashes = ParseHashes( request )
|
|
|
|
#
|
|
|
|
service_keys_to_tags = None
|
|
|
|
if 'service_keys_to_tags' in request.parsed_request_args:
|
|
|
|
service_keys_to_tags = request.parsed_request_args.GetValue( 'service_keys_to_tags', dict )
|
|
|
|
elif 'service_names_to_tags' in request.parsed_request_args:
|
|
|
|
service_names_to_tags = request.parsed_request_args.GetValue( 'service_names_to_tags', dict )
|
|
|
|
service_keys_to_tags = ConvertServiceNamesDictToKeys( HC.REAL_TAG_SERVICES, service_names_to_tags )
|
|
|
|
|
|
service_keys_to_actions_to_tags = None
|
|
|
|
if service_keys_to_tags is not None:
|
|
|
|
service_keys_to_actions_to_tags = {}
|
|
|
|
for ( service_key, tags ) in service_keys_to_tags.items():
|
|
|
|
try:
|
|
|
|
service = HG.client_controller.services_manager.GetService( service_key )
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Could not find the service with key {}! Maybe it was recently deleted?'.format( service_key.hex() ) )
|
|
|
|
|
|
if service.GetServiceType() == HC.LOCAL_TAG:
|
|
|
|
content_action = HC.CONTENT_UPDATE_ADD
|
|
|
|
else:
|
|
|
|
content_action = HC.CONTENT_UPDATE_PEND
|
|
|
|
|
|
service_keys_to_actions_to_tags[ service_key ] = collections.defaultdict( set )
|
|
|
|
service_keys_to_actions_to_tags[ service_key ][ content_action ].update( tags )
|
|
|
|
|
|
|
|
if 'service_keys_to_actions_to_tags' in request.parsed_request_args:
|
|
|
|
service_keys_to_actions_to_tags = request.parsed_request_args.GetValue( 'service_keys_to_actions_to_tags', dict )
|
|
|
|
elif 'service_names_to_actions_to_tags' in request.parsed_request_args:
|
|
|
|
service_names_to_actions_to_tags = request.parsed_request_args.GetValue( 'service_names_to_actions_to_tags', dict )
|
|
|
|
service_keys_to_actions_to_tags = ConvertServiceNamesDictToKeys( HC.REAL_TAG_SERVICES, service_names_to_actions_to_tags )
|
|
|
|
|
|
if service_keys_to_actions_to_tags is None:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Need a service-names-to-tags parameter!' )
|
|
|
|
|
|
service_keys_to_content_updates = collections.defaultdict( list )
|
|
|
|
for ( service_key, actions_to_tags ) in service_keys_to_actions_to_tags.items():
|
|
|
|
try:
|
|
|
|
service = HG.client_controller.services_manager.GetService( service_key )
|
|
|
|
except HydrusExceptions.DataMissing:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Could not find the service with key {}! Maybe it was recently deleted?'.format( service_key.hex() ) )
|
|
|
|
|
|
if service.GetServiceType() not in HC.REAL_TAG_SERVICES:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Was given a service that is not a tag service!' )
|
|
|
|
|
|
for ( content_action, tags ) in actions_to_tags.items():
|
|
|
|
tags = list( tags )
|
|
|
|
if len( tags ) == 0:
|
|
|
|
continue
|
|
|
|
|
|
content_action = int( content_action )
|
|
|
|
actual_tags = []
|
|
|
|
tags_to_reasons = {}
|
|
|
|
for tag_item in tags:
|
|
|
|
reason = 'Petitioned from API'
|
|
|
|
if isinstance( tag_item, str ):
|
|
|
|
tag = tag_item
|
|
|
|
elif isinstance( tag_item, collections.abc.Collection ) and len( tag_item ) == 2:
|
|
|
|
( tag, reason ) = tag_item
|
|
|
|
if not ( isinstance( tag, str ) and isinstance( reason, str ) ):
|
|
|
|
continue
|
|
|
|
|
|
else:
|
|
|
|
continue
|
|
|
|
|
|
actual_tags.append( tag )
|
|
tags_to_reasons[ tag ] = reason
|
|
|
|
|
|
actual_tags = HydrusTags.CleanTags( actual_tags )
|
|
|
|
if len( actual_tags ) == 0:
|
|
|
|
continue
|
|
|
|
|
|
tags = actual_tags
|
|
|
|
if service.GetServiceType() == HC.LOCAL_TAG:
|
|
|
|
if content_action not in ( HC.CONTENT_UPDATE_ADD, HC.CONTENT_UPDATE_DELETE ):
|
|
|
|
continue
|
|
|
|
|
|
else:
|
|
|
|
if content_action in ( HC.CONTENT_UPDATE_ADD, HC.CONTENT_UPDATE_DELETE ):
|
|
|
|
continue
|
|
|
|
|
|
|
|
if content_action == HC.CONTENT_UPDATE_PETITION:
|
|
|
|
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, content_action, ( tag, hashes ), reason = tags_to_reasons[ tag ] ) for tag in tags ]
|
|
|
|
else:
|
|
|
|
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, content_action, ( tag, hashes ) ) for tag in tags ]
|
|
|
|
|
|
service_keys_to_content_updates[ service_key ].extend( content_updates )
|
|
|
|
|
|
|
|
if len( service_keys_to_content_updates ) > 0:
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddTagsGetTagServices( HydrusResourceClientAPIRestrictedAddTags ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
local_tags = HG.client_controller.services_manager.GetServices( ( HC.LOCAL_TAG, ) )
|
|
tag_repos = HG.client_controller.services_manager.GetServices( ( HC.TAG_REPOSITORY, ) )
|
|
|
|
body_dict = {}
|
|
|
|
body_dict[ 'local_tags' ] = [ service.GetName() for service in local_tags ]
|
|
body_dict[ 'tag_repositories' ] = [ service.GetName() for service in tag_repos ]
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddTagsSearchTags( HydrusResourceClientAPIRestrictedAddTags ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_SEARCH_FILES )
|
|
|
|
|
|
def _GetParsedAutocompleteText( self, search, tag_service_key ) -> ClientSearch.ParsedAutocompleteText:
|
|
|
|
tag_autocomplete_options = HG.client_controller.tag_display_manager.GetTagAutocompleteOptions( tag_service_key )
|
|
|
|
collapse_search_characters = True
|
|
|
|
parsed_autocomplete_text = ClientSearch.ParsedAutocompleteText( search, tag_autocomplete_options, collapse_search_characters )
|
|
|
|
parsed_autocomplete_text.SetInclusive( True )
|
|
|
|
return parsed_autocomplete_text
|
|
|
|
|
|
def _GetTagServiceKey( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
tag_service_key = CC.COMBINED_TAG_SERVICE_KEY
|
|
|
|
if 'tag_service_key' in request.parsed_request_args:
|
|
|
|
tag_service_key = request.parsed_request_args[ 'tag_service_key' ]
|
|
|
|
elif 'tag_service_name' in request.parsed_request_args:
|
|
|
|
tag_service_name = request.parsed_request_args[ 'tag_service_name' ]
|
|
|
|
try:
|
|
|
|
tag_service_key = HG.client_controller.services_manager.GetServiceKeyFromName( HC.ALL_TAG_SERVICES, tag_service_name )
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Could not find the service "{}"!'.format( tag_service_name ) )
|
|
|
|
|
|
try:
|
|
|
|
service = HG.client_controller.services_manager.GetService( tag_service_key )
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Could not find that tag service!' )
|
|
|
|
if service.GetServiceType() not in HC.ALL_TAG_SERVICES:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Sorry, that service key did not give a tag service!' )
|
|
|
|
|
|
return tag_service_key
|
|
|
|
|
|
def _GetTagMatches( self, request: HydrusServerRequest.HydrusRequest, tag_display_type: int, tag_service_key: bytes, parsed_autocomplete_text: ClientSearch.ParsedAutocompleteText ) -> typing.List[ ClientSearch.Predicate ]:
|
|
|
|
matches = []
|
|
|
|
if parsed_autocomplete_text.IsAcceptableForTagSearches():
|
|
|
|
tag_context = ClientSearch.TagContext( service_key = tag_service_key )
|
|
|
|
autocomplete_search_text = parsed_autocomplete_text.GetSearchText( True )
|
|
|
|
default_location_context = HG.client_controller.new_options.GetDefaultLocalLocationContext()
|
|
|
|
file_search_context = ClientSearch.FileSearchContext( location_context = default_location_context, tag_context = tag_context )
|
|
|
|
job_key = ClientThreading.JobKey( cancellable = True )
|
|
|
|
request.disconnect_callables.append( job_key.Cancel )
|
|
|
|
search_namespaces_into_full_tags = parsed_autocomplete_text.GetTagAutocompleteOptions().SearchNamespacesIntoFullTags()
|
|
|
|
predicates = HG.client_controller.Read( 'autocomplete_predicates', tag_display_type, file_search_context, search_text = autocomplete_search_text, job_key = job_key, search_namespaces_into_full_tags = search_namespaces_into_full_tags )
|
|
|
|
display_tag_service_key = tag_context.display_service_key
|
|
|
|
matches = ClientSearch.FilterPredicatesBySearchText( display_tag_service_key, autocomplete_search_text, predicates )
|
|
|
|
matches = ClientSearch.SortPredicates( matches )
|
|
|
|
|
|
return matches
|
|
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
search = request.parsed_request_args.GetValue( 'search', str )
|
|
|
|
tag_display_type_str = request.parsed_request_args.GetValue( 'tag_display_type', str, default_value = 'storage' )
|
|
|
|
tag_display_type = ClientTags.TAG_DISPLAY_STORAGE if tag_display_type_str == 'storage' else ClientTags.TAG_DISPLAY_ACTUAL
|
|
|
|
tag_service_key = self._GetTagServiceKey( request )
|
|
|
|
parsed_autocomplete_text = self._GetParsedAutocompleteText( search, tag_service_key )
|
|
|
|
matches = self._GetTagMatches( request, tag_display_type, tag_service_key, parsed_autocomplete_text )
|
|
|
|
matches = request.client_api_permissions.FilterTagPredicateResponse( matches )
|
|
|
|
body_dict = {}
|
|
|
|
# TODO: Ok so we could add sibling/parent info here if the tag display type is storage, or in both cases. probably only if client asks for it
|
|
|
|
tags = [ { 'value' : match.GetValue(), 'count' : match.GetCount().GetMinCount() } for match in matches ]
|
|
|
|
body_dict[ 'tags' ] = tags
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddTagsCleanTags( HydrusResourceClientAPIRestrictedAddTags ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
tags = request.parsed_request_args.GetValue( 'tags', list, expected_list_type = str )
|
|
|
|
tags = list( HydrusTags.CleanTags( tags ) )
|
|
|
|
tags = HydrusTags.SortNumericTags( tags )
|
|
|
|
body_dict = {}
|
|
|
|
body_dict[ 'tags' ] = tags
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddURLs( HydrusResourceClientAPIRestricted ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_ADD_URLS )
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddURLsAssociateURL( HydrusResourceClientAPIRestrictedAddURLs ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
urls_to_add = []
|
|
|
|
if 'url_to_add' in request.parsed_request_args:
|
|
|
|
url = request.parsed_request_args.GetValue( 'url_to_add', str )
|
|
|
|
urls_to_add.append( url )
|
|
|
|
|
|
if 'urls_to_add' in request.parsed_request_args:
|
|
|
|
urls = request.parsed_request_args.GetValue( 'urls_to_add', list, expected_list_type = str )
|
|
|
|
for url in urls:
|
|
|
|
if not isinstance( url, str ):
|
|
|
|
continue
|
|
|
|
|
|
urls_to_add.append( url )
|
|
|
|
|
|
|
|
urls_to_delete = []
|
|
|
|
if 'url_to_delete' in request.parsed_request_args:
|
|
|
|
url = request.parsed_request_args.GetValue( 'url_to_delete', str )
|
|
|
|
urls_to_delete.append( url )
|
|
|
|
|
|
if 'urls_to_delete' in request.parsed_request_args:
|
|
|
|
urls = request.parsed_request_args.GetValue( 'urls_to_delete', list, expected_list_type = str )
|
|
|
|
for url in urls:
|
|
|
|
if not isinstance( url, str ):
|
|
|
|
continue
|
|
|
|
|
|
urls_to_delete.append( url )
|
|
|
|
|
|
|
|
domain_manager = HG.client_controller.network_engine.domain_manager
|
|
|
|
try:
|
|
|
|
urls_to_add = [ domain_manager.NormaliseURL( url ) for url in urls_to_add ]
|
|
|
|
except HydrusExceptions.URLClassException as e:
|
|
|
|
raise HydrusExceptions.BadRequestException( e )
|
|
|
|
|
|
if len( urls_to_add ) == 0 and len( urls_to_delete ) == 0:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Did not find any URLs to add or delete!' )
|
|
|
|
|
|
applicable_hashes = ParseHashes( request )
|
|
|
|
if len( applicable_hashes ) == 0:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Did not find any hashes to apply the urls to!' )
|
|
|
|
|
|
service_keys_to_content_updates = collections.defaultdict( list )
|
|
|
|
if len( urls_to_add ) > 0:
|
|
|
|
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_URLS, HC.CONTENT_UPDATE_ADD, ( urls_to_add, applicable_hashes ) )
|
|
|
|
service_keys_to_content_updates[ CC.COMBINED_LOCAL_FILE_SERVICE_KEY ].append( content_update )
|
|
|
|
|
|
if len( urls_to_delete ) > 0:
|
|
|
|
content_update = HydrusData.ContentUpdate( HC.CONTENT_TYPE_URLS, HC.CONTENT_UPDATE_DELETE, ( urls_to_delete, applicable_hashes ) )
|
|
|
|
service_keys_to_content_updates[ CC.COMBINED_LOCAL_FILE_SERVICE_KEY ].append( content_update )
|
|
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddURLsGetURLFiles( HydrusResourceClientAPIRestrictedAddURLs ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
url = request.parsed_request_args.GetValue( 'url', str )
|
|
|
|
do_file_system_check = request.parsed_request_args.GetValue( 'doublecheck_file_system', bool, default_value = False )
|
|
|
|
if url == '':
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Given URL was empty!' )
|
|
|
|
|
|
try:
|
|
|
|
normalised_url = HG.client_controller.network_engine.domain_manager.NormaliseURL( url )
|
|
|
|
except HydrusExceptions.URLClassException as e:
|
|
|
|
raise HydrusExceptions.BadRequestException( e )
|
|
|
|
|
|
url_statuses = HG.client_controller.Read( 'url_statuses', normalised_url )
|
|
|
|
json_happy_url_statuses = []
|
|
|
|
for file_import_status in url_statuses:
|
|
|
|
if do_file_system_check:
|
|
|
|
file_import_status = ClientImportFiles.CheckFileImportStatus( file_import_status )
|
|
|
|
|
|
d = {}
|
|
|
|
d[ 'status' ] = file_import_status.status
|
|
d[ 'hash' ] = HydrusData.BytesToNoneOrHex( file_import_status.hash )
|
|
d[ 'note' ] = file_import_status.note
|
|
|
|
json_happy_url_statuses.append( d )
|
|
|
|
|
|
body_dict = { 'normalised_url' : normalised_url, 'url_file_statuses' : json_happy_url_statuses }
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddURLsGetURLInfo( HydrusResourceClientAPIRestrictedAddURLs ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
url = request.parsed_request_args.GetValue( 'url', str )
|
|
|
|
if url == '':
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Given URL was empty!' )
|
|
|
|
|
|
try:
|
|
|
|
normalised_url = HG.client_controller.network_engine.domain_manager.NormaliseURL( url )
|
|
|
|
( url_type, match_name, can_parse, cannot_parse_reason ) = HG.client_controller.network_engine.domain_manager.GetURLParseCapability( normalised_url )
|
|
|
|
except HydrusExceptions.URLClassException as e:
|
|
|
|
raise HydrusExceptions.BadRequestException( e )
|
|
|
|
|
|
body_dict = { 'normalised_url' : normalised_url, 'url_type' : url_type, 'url_type_string' : HC.url_type_string_lookup[ url_type ], 'match_name' : match_name, 'can_parse' : can_parse }
|
|
|
|
if not can_parse:
|
|
|
|
body_dict[ 'cannot_parse_reason' ] = cannot_parse_reason
|
|
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedAddURLsImportURL( HydrusResourceClientAPIRestrictedAddURLs ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
url = request.parsed_request_args.GetValue( 'url', str )
|
|
|
|
if url == '':
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Given URL was empty!' )
|
|
|
|
|
|
filterable_tags = set()
|
|
|
|
if 'filterable_tags' in request.parsed_request_args:
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_ADD_TAGS )
|
|
|
|
filterable_tags = request.parsed_request_args.GetValue( 'filterable_tags', list, expected_list_type = str )
|
|
|
|
filterable_tags = HydrusTags.CleanTags( filterable_tags )
|
|
|
|
|
|
additional_service_keys_to_tags = ClientTags.ServiceKeysToTags()
|
|
|
|
service_keys_to_additional_tags = None
|
|
|
|
if 'service_names_to_tags' in request.parsed_request_args or 'service_names_to_additional_tags' in request.parsed_request_args:
|
|
|
|
if 'service_names_to_tags' in request.parsed_request_args:
|
|
|
|
service_names_to_additional_tags = request.parsed_request_args.GetValue( 'service_names_to_tags', dict )
|
|
|
|
else:
|
|
|
|
service_names_to_additional_tags = request.parsed_request_args.GetValue( 'service_names_to_additional_tags', dict )
|
|
|
|
|
|
service_keys_to_additional_tags = ConvertServiceNamesDictToKeys( HC.REAL_TAG_SERVICES, service_names_to_additional_tags )
|
|
|
|
elif 'service_keys_to_additional_tags' in request.parsed_request_args:
|
|
|
|
service_keys_to_additional_tags = request.parsed_request_args.GetValue( 'service_keys_to_additional_tags', dict )
|
|
|
|
|
|
if service_keys_to_additional_tags is not None:
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_ADD_TAGS )
|
|
|
|
for ( service_key, tags ) in service_keys_to_additional_tags.items():
|
|
|
|
service = HG.client_controller.services_manager.GetService( service_key )
|
|
|
|
if service.GetServiceType() not in HC.REAL_TAG_SERVICES:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Was given a service that is not a tag service!' )
|
|
|
|
|
|
tags = HydrusTags.CleanTags( tags )
|
|
|
|
if len( tags ) == 0:
|
|
|
|
continue
|
|
|
|
|
|
additional_service_keys_to_tags[ service_key ] = tags
|
|
|
|
|
|
|
|
destination_page_name = None
|
|
|
|
if 'destination_page_name' in request.parsed_request_args:
|
|
|
|
destination_page_name = request.parsed_request_args.GetValue( 'destination_page_name', str )
|
|
|
|
|
|
destination_page_key = None
|
|
|
|
if 'destination_page_key' in request.parsed_request_args:
|
|
|
|
destination_page_key = request.parsed_request_args.GetValue( 'destination_page_key', bytes )
|
|
|
|
|
|
show_destination_page = request.parsed_request_args.GetValue( 'show_destination_page', bool, default_value = False )
|
|
|
|
def do_it():
|
|
|
|
return HG.client_controller.gui.ImportURLFromAPI( url, filterable_tags, additional_service_keys_to_tags, destination_page_name, destination_page_key, show_destination_page )
|
|
|
|
|
|
try:
|
|
|
|
( normalised_url, result_text ) = HG.client_controller.CallBlockingToQt( HG.client_controller.gui, do_it )
|
|
|
|
except HydrusExceptions.URLClassException as e:
|
|
|
|
raise HydrusExceptions.BadRequestException( e )
|
|
|
|
|
|
time.sleep( 0.05 ) # yield and give the ui time to catch up with new URL pubsubs in case this is being spammed
|
|
|
|
body_dict = { 'human_result_text' : result_text, 'normalised_url' : normalised_url }
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedGetFiles( HydrusResourceClientAPIRestricted ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_SEARCH_FILES )
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedGetFilesSearchFiles( HydrusResourceClientAPIRestrictedGetFiles ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
location_context = ParseLocationContext( request, ClientLocation.LocationContext.STATICCreateSimple( CC.COMBINED_LOCAL_MEDIA_SERVICE_KEY ) )
|
|
|
|
if 'tag_service_key' in request.parsed_request_args or 'tag_service_name' in request.parsed_request_args:
|
|
|
|
if 'tag_service_key' in request.parsed_request_args:
|
|
|
|
tag_service_key = request.parsed_request_args[ 'tag_service_key' ]
|
|
|
|
else:
|
|
|
|
tag_service_name = request.parsed_request_args[ 'tag_service_name' ]
|
|
|
|
try:
|
|
|
|
tag_service_key = HG.client_controller.services_manager.GetServiceKeyFromName( HC.ALL_TAG_SERVICES, tag_service_name )
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Could not find the service "{}"!'.format( tag_service_name ) )
|
|
|
|
|
|
|
|
try:
|
|
|
|
service = HG.client_controller.services_manager.GetService( tag_service_key )
|
|
|
|
except:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Could not find that tag service!' )
|
|
|
|
|
|
if service.GetServiceType() not in HC.ALL_TAG_SERVICES:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Sorry, that service key did not give a tag service!' )
|
|
|
|
|
|
else:
|
|
|
|
tag_service_key = CC.COMBINED_TAG_SERVICE_KEY
|
|
|
|
|
|
if tag_service_key == CC.COMBINED_TAG_SERVICE_KEY and location_context.IsAllKnownFiles():
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Sorry, search for all known tags over all known files is not supported!' )
|
|
|
|
|
|
tag_context = ClientSearch.TagContext( service_key = tag_service_key )
|
|
predicates = ParseClientAPISearchPredicates( request )
|
|
|
|
return_hashes = False
|
|
return_file_ids = True
|
|
|
|
if len( predicates ) == 0:
|
|
|
|
hash_ids = []
|
|
|
|
else:
|
|
|
|
file_search_context = ClientSearch.FileSearchContext( location_context = location_context, tag_context = tag_context, predicates = predicates )
|
|
|
|
file_sort_type = CC.SORT_FILES_BY_IMPORT_TIME
|
|
|
|
if 'file_sort_type' in request.parsed_request_args:
|
|
|
|
file_sort_type = request.parsed_request_args[ 'file_sort_type' ]
|
|
|
|
|
|
if file_sort_type not in CC.SYSTEM_SORT_TYPES:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Sorry, did not understand that sort type!' )
|
|
|
|
|
|
file_sort_asc = False
|
|
|
|
if 'file_sort_asc' in request.parsed_request_args:
|
|
|
|
file_sort_asc = request.parsed_request_args.GetValue( 'file_sort_asc', bool )
|
|
|
|
|
|
sort_order = CC.SORT_ASC if file_sort_asc else CC.SORT_DESC
|
|
|
|
# newest first
|
|
sort_by = ClientMedia.MediaSort( sort_type = ( 'system', file_sort_type ), sort_order = sort_order )
|
|
|
|
if 'return_hashes' in request.parsed_request_args:
|
|
|
|
return_hashes = request.parsed_request_args.GetValue( 'return_hashes', bool )
|
|
|
|
|
|
if 'return_file_ids' in request.parsed_request_args:
|
|
|
|
return_file_ids = request.parsed_request_args.GetValue( 'return_file_ids', bool )
|
|
|
|
|
|
job_key = ClientThreading.JobKey( cancellable = True )
|
|
|
|
request.disconnect_callables.append( job_key.Cancel )
|
|
|
|
hash_ids = HG.client_controller.Read( 'file_query_ids', file_search_context, job_key = job_key, sort_by = sort_by, apply_implicit_limit = False )
|
|
|
|
|
|
request.client_api_permissions.SetLastSearchResults( hash_ids )
|
|
|
|
body_dict = {}
|
|
|
|
if return_hashes:
|
|
|
|
hash_ids_to_hashes = HG.client_controller.Read( 'hash_ids_to_hashes', hash_ids = hash_ids )
|
|
|
|
# maintain sort
|
|
body_dict[ 'hashes' ] = [ hash_ids_to_hashes[ hash_id ].hex() for hash_id in hash_ids ]
|
|
|
|
|
|
if return_file_ids:
|
|
|
|
body_dict[ 'file_ids' ] = list( hash_ids )
|
|
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedGetFilesGetFile( HydrusResourceClientAPIRestrictedGetFiles ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
try:
|
|
|
|
if 'file_id' in request.parsed_request_args:
|
|
|
|
file_id = request.parsed_request_args.GetValue( 'file_id', int )
|
|
|
|
request.client_api_permissions.CheckPermissionToSeeFiles( ( file_id, ) )
|
|
|
|
( media_result, ) = HG.client_controller.Read( 'media_results_from_ids', ( file_id, ) )
|
|
|
|
elif 'hash' in request.parsed_request_args:
|
|
|
|
request.client_api_permissions.CheckCanSeeAllFiles()
|
|
|
|
hash = request.parsed_request_args.GetValue( 'hash', bytes )
|
|
|
|
media_result = HG.client_controller.Read( 'media_result', hash )
|
|
|
|
else:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Please include a file_id or hash parameter!' )
|
|
|
|
|
|
except HydrusExceptions.DataMissing as e:
|
|
|
|
raise HydrusExceptions.NotFoundException( 'One or more of those file identifiers was missing!' )
|
|
|
|
|
|
try:
|
|
|
|
hash = media_result.GetHash()
|
|
mime = media_result.GetMime()
|
|
|
|
path = HG.client_controller.client_files_manager.GetFilePath( hash, mime )
|
|
|
|
if not os.path.exists( path ):
|
|
|
|
raise HydrusExceptions.FileMissingException()
|
|
|
|
|
|
except HydrusExceptions.FileMissingException:
|
|
|
|
raise HydrusExceptions.NotFoundException( 'Could not find that file!' )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = mime, path = path )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedGetFilesFileHashes( HydrusResourceClientAPIRestrictedGetFiles ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
supported_hash_types = ( 'sha256', 'md5', 'sha1', 'sha512' )
|
|
|
|
source_hash_type = request.parsed_request_args.GetValue( 'source_hash_type', str, default_value = 'sha256' )
|
|
|
|
if source_hash_type not in supported_hash_types:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'I do not support that hash type!' )
|
|
|
|
|
|
desired_hash_type = request.parsed_request_args.GetValue( 'desired_hash_type', str )
|
|
|
|
if desired_hash_type not in supported_hash_types:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'I do not support that hash type!' )
|
|
|
|
|
|
source_hashes = set()
|
|
|
|
if 'hash' in request.parsed_request_args:
|
|
|
|
request_hash = request.parsed_request_args.GetValue( 'hash', bytes )
|
|
|
|
source_hashes.add( request_hash )
|
|
|
|
|
|
if 'hashes' in request.parsed_request_args:
|
|
|
|
request_hashes = request.parsed_request_args.GetValue( 'hashes', list, expected_list_type = bytes )
|
|
|
|
source_hashes.update( request_hashes )
|
|
|
|
|
|
if len( source_hashes ) == 0:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'You have to specify a hash to look up!' )
|
|
|
|
|
|
CheckHashLength( source_hashes, hash_type = source_hash_type )
|
|
|
|
source_to_desired = HG.client_controller.Read( 'file_hashes', source_hashes, source_hash_type, desired_hash_type )
|
|
|
|
encoded_source_to_desired = { source_hash.hex() : desired_hash.hex() for ( source_hash, desired_hash ) in source_to_desired.items() }
|
|
|
|
body_dict = {
|
|
'hashes' : encoded_source_to_desired
|
|
}
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedGetFilesFileMetadata( HydrusResourceClientAPIRestrictedGetFiles ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
missing_hashes = set()
|
|
|
|
only_return_identifiers = request.parsed_request_args.GetValue( 'only_return_identifiers', bool, default_value = False )
|
|
only_return_basic_information = request.parsed_request_args.GetValue( 'only_return_basic_information', bool, default_value = False )
|
|
hide_service_names_tags = request.parsed_request_args.GetValue( 'hide_service_names_tags', bool, default_value = True )
|
|
hide_service_keys_tags = request.parsed_request_args.GetValue( 'hide_service_names_tags', bool, default_value = False )
|
|
detailed_url_information = request.parsed_request_args.GetValue( 'detailed_url_information', bool, default_value = False )
|
|
include_notes = request.parsed_request_args.GetValue( 'include_notes', bool, default_value = False )
|
|
create_new_file_ids = request.parsed_request_args.GetValue( 'create_new_file_ids', bool, default_value = False )
|
|
|
|
if 'file_ids' in request.parsed_request_args or 'file_id' in request.parsed_request_args:
|
|
|
|
if 'file_ids' in request.parsed_request_args:
|
|
|
|
file_ids = request.parsed_request_args.GetValue( 'file_ids', list, expected_list_type = int )
|
|
|
|
else:
|
|
|
|
file_ids = [ request.parsed_request_args.GetValue( 'file_id', int ) ]
|
|
|
|
request.client_api_permissions.CheckPermissionToSeeFiles( file_ids )
|
|
|
|
elif 'hashes' in request.parsed_request_args or 'hash' in request.parsed_request_args:
|
|
|
|
request.client_api_permissions.CheckCanSeeAllFiles()
|
|
|
|
if 'hashes' in request.parsed_request_args:
|
|
|
|
hashes = request.parsed_request_args.GetValue( 'hashes', list, expected_list_type = bytes )
|
|
|
|
else:
|
|
|
|
hashes = [ request.parsed_request_args.GetValue( 'hash', bytes ) ]
|
|
|
|
|
|
hashes = HydrusData.DedupeList( hashes )
|
|
|
|
CheckHashLength( hashes )
|
|
|
|
file_ids_to_hashes = HG.client_controller.Read( 'hash_ids_to_hashes', hashes = hashes, create_new_hash_ids = create_new_file_ids )
|
|
|
|
file_ids = set( file_ids_to_hashes.keys() )
|
|
|
|
if len( file_ids_to_hashes ) < len( hashes ):
|
|
|
|
missing_hashes = set( hashes ).difference( file_ids_to_hashes.values() )
|
|
|
|
|
|
else:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Please include a file_ids or hashes parameter!' )
|
|
|
|
|
|
try:
|
|
|
|
if only_return_identifiers:
|
|
|
|
file_ids_to_hashes = HG.client_controller.Read( 'hash_ids_to_hashes', hash_ids = file_ids )
|
|
|
|
elif only_return_basic_information:
|
|
|
|
file_info_managers = HG.client_controller.Read( 'file_info_managers_from_ids', file_ids, sorted = True )
|
|
|
|
else:
|
|
|
|
media_results = HG.client_controller.Read( 'media_results_from_ids', file_ids, sorted = True )
|
|
|
|
|
|
except HydrusExceptions.DataMissing as e:
|
|
|
|
raise HydrusExceptions.NotFoundException( 'One or more of those file identifiers did not exist in the database!' )
|
|
|
|
|
|
body_dict = {}
|
|
|
|
metadata = []
|
|
|
|
for hash in missing_hashes:
|
|
|
|
metadata_row = {
|
|
'file_id' : None,
|
|
'hash' : hash.hex()
|
|
}
|
|
|
|
metadata.append( metadata_row )
|
|
|
|
|
|
if only_return_identifiers:
|
|
|
|
for ( file_id, hash ) in file_ids_to_hashes.items():
|
|
|
|
metadata_row = {
|
|
'file_id' : file_id,
|
|
'hash' : hash.hex()
|
|
}
|
|
|
|
metadata.append( metadata_row )
|
|
|
|
|
|
elif only_return_basic_information:
|
|
|
|
for file_info_manager in file_info_managers:
|
|
|
|
metadata_row = {
|
|
'file_id' : file_info_manager.hash_id,
|
|
'hash' : file_info_manager.hash.hex(),
|
|
'size' : file_info_manager.size,
|
|
'mime' : HC.mime_mimetype_string_lookup[ file_info_manager.mime ],
|
|
'ext' : HC.mime_ext_lookup[ file_info_manager.mime ],
|
|
'width' : file_info_manager.width,
|
|
'height' : file_info_manager.height,
|
|
'duration' : file_info_manager.duration,
|
|
'num_frames' : file_info_manager.num_frames,
|
|
'num_words' : file_info_manager.num_words,
|
|
'has_audio' : file_info_manager.has_audio
|
|
}
|
|
|
|
metadata.append( metadata_row )
|
|
|
|
|
|
else:
|
|
|
|
services_manager = HG.client_controller.services_manager
|
|
|
|
tag_service_keys = services_manager.GetServiceKeys( HC.ALL_TAG_SERVICES )
|
|
service_keys_to_types = { service.GetServiceKey() : service.GetServiceType() for service in services_manager.GetServices() }
|
|
service_keys_to_names = services_manager.GetServiceKeysToNames()
|
|
|
|
ipfs_service_keys = services_manager.GetServiceKeys( ( HC.IPFS, ) )
|
|
|
|
thumbnail_bounding_dimensions = HG.client_controller.options[ 'thumbnail_dimensions' ]
|
|
thumbnail_scale_type = HG.client_controller.new_options.GetInteger( 'thumbnail_scale_type' )
|
|
thumbnail_dpr_percent = HG.client_controller.new_options.GetInteger( 'thumbnail_dpr_percent' )
|
|
|
|
for media_result in media_results:
|
|
|
|
file_info_manager = media_result.GetFileInfoManager()
|
|
|
|
mime = file_info_manager.mime
|
|
width = file_info_manager.width
|
|
height = file_info_manager.height
|
|
|
|
metadata_row = {
|
|
'file_id' : file_info_manager.hash_id,
|
|
'hash' : file_info_manager.hash.hex(),
|
|
'size' : file_info_manager.size,
|
|
'mime' : HC.mime_mimetype_string_lookup[ mime ],
|
|
'ext' : HC.mime_ext_lookup[ mime ],
|
|
'width' : width,
|
|
'height' : height,
|
|
'duration' : file_info_manager.duration,
|
|
'num_frames' : file_info_manager.num_frames,
|
|
'num_words' : file_info_manager.num_words,
|
|
'has_audio' : file_info_manager.has_audio
|
|
}
|
|
|
|
if file_info_manager.mime in HC.MIMES_WITH_THUMBNAILS:
|
|
|
|
if width is not None and height is not None and width > 0 and height > 0:
|
|
|
|
( clip_rect, ( expected_thumbnail_width, expected_thumbnail_height ) ) = HydrusImageHandling.GetThumbnailResolutionAndClipRegion( ( width, height ), thumbnail_bounding_dimensions, thumbnail_scale_type, thumbnail_dpr_percent )
|
|
|
|
metadata_row[ 'thumbnail_width' ] = expected_thumbnail_width
|
|
metadata_row[ 'thumbnail_height' ] = expected_thumbnail_height
|
|
|
|
|
|
|
|
if include_notes:
|
|
|
|
metadata_row[ 'notes' ] = media_result.GetNotesManager().GetNamesToNotes()
|
|
|
|
locations_manager = media_result.GetLocationsManager()
|
|
|
|
metadata_row[ 'file_services' ] = {
|
|
'current' : {},
|
|
'deleted' : {}
|
|
}
|
|
|
|
current = locations_manager.GetCurrent()
|
|
|
|
for file_service_key in current:
|
|
|
|
timestamp = locations_manager.GetCurrentTimestamp( file_service_key )
|
|
|
|
metadata_row[ 'file_services' ][ 'current' ][ file_service_key.hex() ] = {
|
|
'name' : service_keys_to_names[ file_service_key ],
|
|
'type' : service_keys_to_types[ file_service_key ],
|
|
'type_pretty' : HC.service_string_lookup[ service_keys_to_types[ file_service_key ] ],
|
|
'time_imported' : timestamp
|
|
}
|
|
|
|
|
|
deleted = locations_manager.GetDeleted()
|
|
|
|
for file_service_key in deleted:
|
|
|
|
( timestamp, original_timestamp ) = locations_manager.GetDeletedTimestamps( file_service_key )
|
|
|
|
metadata_row[ 'file_services' ][ 'deleted' ][ file_service_key.hex() ] = {
|
|
'name' : service_keys_to_names[ file_service_key ],
|
|
'type' : service_keys_to_types[ file_service_key ],
|
|
'type_pretty' : HC.service_string_lookup[ service_keys_to_types[ file_service_key ] ],
|
|
'time_deleted' : timestamp,
|
|
'time_imported' : original_timestamp
|
|
}
|
|
|
|
|
|
timestamp_manager = locations_manager.GetTimestampManager()
|
|
|
|
metadata_row[ 'time_modified' ] = timestamp_manager.GetAggregateModifiedTimestamp()
|
|
|
|
time_modified_details = timestamp_manager.GetDomainModifiedTimestamps()
|
|
|
|
local_modified = timestamp_manager.GetFileModifiedTimestamp()
|
|
|
|
if local_modified is not None:
|
|
|
|
time_modified_details[ 'local' ] = local_modified
|
|
|
|
|
|
metadata_row[ 'time_modified_details' ] = time_modified_details
|
|
|
|
metadata_row[ 'is_inbox' ] = locations_manager.inbox
|
|
metadata_row[ 'is_local' ] = locations_manager.IsLocal()
|
|
metadata_row[ 'is_trashed' ] = locations_manager.IsTrashed()
|
|
metadata_row[ 'is_deleted' ] = CC.COMBINED_LOCAL_MEDIA_SERVICE_KEY in locations_manager.GetDeleted() or locations_manager.IsTrashed()
|
|
|
|
metadata_row[ 'has_exif' ] = file_info_manager.has_exif
|
|
metadata_row[ 'has_human_readable_embedded_metadata' ] = file_info_manager.has_human_readable_embedded_metadata
|
|
metadata_row[ 'has_icc_profile' ] = file_info_manager.has_icc_profile
|
|
|
|
known_urls = sorted( locations_manager.GetURLs() )
|
|
|
|
metadata_row[ 'known_urls' ] = known_urls
|
|
|
|
metadata_row[ 'ipfs_multihashes' ] = { ipfs_service_key.hex() : multihash for ( ipfs_service_key, multihash ) in locations_manager.GetServiceFilenames().items() if ipfs_service_key in ipfs_service_keys }
|
|
|
|
if detailed_url_information:
|
|
|
|
detailed_known_urls = []
|
|
|
|
for known_url in known_urls:
|
|
|
|
try:
|
|
|
|
normalised_url = HG.client_controller.network_engine.domain_manager.NormaliseURL( known_url )
|
|
|
|
( url_type, match_name, can_parse, cannot_parse_reason ) = HG.client_controller.network_engine.domain_manager.GetURLParseCapability( normalised_url )
|
|
|
|
except HydrusExceptions.URLClassException as e:
|
|
|
|
continue
|
|
|
|
|
|
detailed_dict = { 'normalised_url' : normalised_url, 'url_type' : url_type, 'url_type_string' : HC.url_type_string_lookup[ url_type ], 'match_name' : match_name, 'can_parse' : can_parse }
|
|
|
|
if not can_parse:
|
|
|
|
detailed_dict[ 'cannot_parse_reason' ] = cannot_parse_reason
|
|
|
|
|
|
detailed_known_urls.append( detailed_dict )
|
|
|
|
|
|
metadata_row[ 'detailed_known_urls' ] = detailed_known_urls
|
|
|
|
|
|
tags_manager = media_result.GetTagsManager()
|
|
|
|
tags_dict = {}
|
|
|
|
for tag_service_key in tag_service_keys:
|
|
|
|
storage_statuses_to_tags = tags_manager.GetStatusesToTags( tag_service_key, ClientTags.TAG_DISPLAY_STORAGE )
|
|
|
|
storage_tags_json_serialisable = { str( status ) : sorted( tags, key = HydrusTags.ConvertTagToSortable ) for ( status, tags ) in storage_statuses_to_tags.items() if len( tags ) > 0 }
|
|
|
|
display_statuses_to_tags = tags_manager.GetStatusesToTags( tag_service_key, ClientTags.TAG_DISPLAY_ACTUAL )
|
|
|
|
display_tags_json_serialisable = { str( status ) : sorted( tags, key = HydrusTags.ConvertTagToSortable ) for ( status, tags ) in display_statuses_to_tags.items() if len( tags ) > 0 }
|
|
|
|
tags_dict_object = {
|
|
'name' : service_keys_to_names[ tag_service_key ],
|
|
'type' : service_keys_to_types[ tag_service_key ],
|
|
'type_pretty' : HC.service_string_lookup[ service_keys_to_types[ tag_service_key ] ],
|
|
'storage_tags' : storage_tags_json_serialisable,
|
|
'display_tags' : display_tags_json_serialisable
|
|
}
|
|
|
|
tags_dict[ tag_service_key.hex() ] = tags_dict_object
|
|
|
|
|
|
metadata_row[ 'tags' ] = tags_dict
|
|
|
|
# Old stuff starts here
|
|
|
|
service_names_to_statuses_to_tags = {}
|
|
api_service_keys_to_statuses_to_tags = {}
|
|
|
|
service_keys_to_statuses_to_tags = tags_manager.GetServiceKeysToStatusesToTags( ClientTags.TAG_DISPLAY_STORAGE )
|
|
|
|
for ( service_key, statuses_to_tags ) in service_keys_to_statuses_to_tags.items():
|
|
|
|
statuses_to_tags_json_serialisable = { str( status ) : sorted( tags, key = HydrusTags.ConvertTagToSortable ) for ( status, tags ) in statuses_to_tags.items() if len( tags ) > 0 }
|
|
|
|
if len( statuses_to_tags_json_serialisable ) > 0:
|
|
|
|
service_name = service_keys_to_names[ service_key ]
|
|
|
|
service_names_to_statuses_to_tags[ service_name ] = statuses_to_tags_json_serialisable
|
|
|
|
api_service_keys_to_statuses_to_tags[ service_key.hex() ] = statuses_to_tags_json_serialisable
|
|
|
|
|
|
|
|
if not hide_service_names_tags:
|
|
|
|
metadata_row[ 'service_names_to_statuses_to_tags' ] = service_names_to_statuses_to_tags
|
|
|
|
|
|
if not hide_service_keys_tags:
|
|
|
|
metadata_row[ 'service_keys_to_statuses_to_tags' ] = api_service_keys_to_statuses_to_tags
|
|
|
|
|
|
#
|
|
|
|
service_names_to_statuses_to_tags = {}
|
|
api_service_keys_to_statuses_to_tags = {}
|
|
|
|
service_keys_to_statuses_to_tags = tags_manager.GetServiceKeysToStatusesToTags( ClientTags.TAG_DISPLAY_ACTUAL )
|
|
|
|
for ( service_key, statuses_to_tags ) in service_keys_to_statuses_to_tags.items():
|
|
|
|
statuses_to_tags_json_serialisable = { str( status ) : sorted( tags, key = HydrusTags.ConvertTagToSortable ) for ( status, tags ) in statuses_to_tags.items() if len( tags ) > 0 }
|
|
|
|
if len( statuses_to_tags_json_serialisable ) > 0:
|
|
|
|
service_name = service_keys_to_names[ service_key ]
|
|
|
|
service_names_to_statuses_to_tags[ service_name ] = statuses_to_tags_json_serialisable
|
|
|
|
api_service_keys_to_statuses_to_tags[ service_key.hex() ] = statuses_to_tags_json_serialisable
|
|
|
|
|
|
|
|
if not hide_service_names_tags:
|
|
|
|
metadata_row[ 'service_names_to_statuses_to_display_tags' ] = service_names_to_statuses_to_tags
|
|
|
|
|
|
if not hide_service_keys_tags:
|
|
|
|
metadata_row[ 'service_keys_to_statuses_to_display_tags' ] = api_service_keys_to_statuses_to_tags
|
|
|
|
|
|
# old stuff ends here
|
|
|
|
#
|
|
|
|
metadata.append( metadata_row )
|
|
|
|
|
|
|
|
body_dict[ 'metadata' ] = metadata
|
|
|
|
mime = request.preferred_mime
|
|
body = Dumps( body_dict, mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedGetFilesGetThumbnail( HydrusResourceClientAPIRestrictedGetFiles ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
try:
|
|
|
|
if 'file_id' in request.parsed_request_args:
|
|
|
|
file_id = request.parsed_request_args.GetValue( 'file_id', int )
|
|
|
|
request.client_api_permissions.CheckPermissionToSeeFiles( ( file_id, ) )
|
|
|
|
( media_result, ) = HG.client_controller.Read( 'media_results_from_ids', ( file_id, ) )
|
|
|
|
elif 'hash' in request.parsed_request_args:
|
|
|
|
request.client_api_permissions.CheckCanSeeAllFiles()
|
|
|
|
hash = request.parsed_request_args.GetValue( 'hash', bytes )
|
|
|
|
media_result = HG.client_controller.Read( 'media_result', hash )
|
|
|
|
else:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'Please include a file_id or hash parameter!' )
|
|
|
|
|
|
except HydrusExceptions.DataMissing as e:
|
|
|
|
raise HydrusExceptions.NotFoundException( 'One or more of those file identifiers was missing!' )
|
|
|
|
|
|
try:
|
|
|
|
path = HG.client_controller.client_files_manager.GetThumbnailPath( media_result )
|
|
|
|
if not os.path.exists( path ):
|
|
|
|
# not _supposed_ to happen, but it seems in odd situations it can
|
|
raise HydrusExceptions.FileMissingException()
|
|
|
|
|
|
except HydrusExceptions.FileMissingException:
|
|
|
|
path = HydrusPaths.mimes_to_default_thumbnail_paths[ media_result.GetMime() ]
|
|
|
|
|
|
mime = HydrusFileHandling.GetThumbnailMime( path )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = mime, path = path )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManageCookies( HydrusResourceClientAPIRestricted ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_MANAGE_COOKIES )
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManageCookiesGetCookies( HydrusResourceClientAPIRestrictedManageCookies ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
domain = request.parsed_request_args.GetValue( 'domain', str )
|
|
|
|
if '.' not in domain:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'The value "{}" does not seem to be a domain!'.format( domain ) )
|
|
|
|
|
|
network_context = ClientNetworkingContexts.NetworkContext( CC.NETWORK_CONTEXT_DOMAIN, domain )
|
|
|
|
session = HG.client_controller.network_engine.session_manager.GetSession( network_context )
|
|
|
|
body_cookies_list = []
|
|
|
|
for cookie in session.cookies:
|
|
|
|
name = cookie.name
|
|
value = cookie.value
|
|
domain = cookie.domain
|
|
path = cookie.path
|
|
expires = cookie.expires
|
|
|
|
body_cookies_list.append( [ name, value, domain, path, expires ] )
|
|
|
|
|
|
body_dict = {}
|
|
|
|
body_dict = { 'cookies' : body_cookies_list }
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManageCookiesSetCookies( HydrusResourceClientAPIRestrictedManageCookies ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
cookie_rows = request.parsed_request_args.GetValue( 'cookies', list )
|
|
|
|
domains_cleared = set()
|
|
domains_set = set()
|
|
|
|
for cookie_row in cookie_rows:
|
|
|
|
if len( cookie_row ) != 5:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'The cookie "{}" did not come in the format [ name, value, domain, path, expires ]!'.format( cookie_row ) )
|
|
|
|
|
|
( name, value, domain, path, expires ) = cookie_row
|
|
|
|
ndp_bad = True in ( not isinstance( var, str ) for var in ( name, domain, path ) )
|
|
v_bad = value is not None and not isinstance( value, str )
|
|
e_bad = expires is not None and not isinstance( expires, int )
|
|
|
|
if ndp_bad or v_bad or e_bad:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'In the row [ name, value, domain, path, expires ], which I received as "{}", name, domain, and path need to be strings, value needs to be null or a string, and expires needs to be null or an integer!'.format( cookie_row ) )
|
|
|
|
|
|
network_context = ClientNetworkingContexts.NetworkContext( CC.NETWORK_CONTEXT_DOMAIN, domain )
|
|
|
|
session = HG.client_controller.network_engine.session_manager.GetSession( network_context )
|
|
|
|
if value is None:
|
|
|
|
domains_cleared.add( domain )
|
|
|
|
session.cookies.clear( domain, path, name )
|
|
|
|
else:
|
|
|
|
domains_set.add( domain )
|
|
|
|
ClientNetworkingFunctions.AddCookieToSession( session, name, value, domain, path, expires )
|
|
|
|
|
|
HG.client_controller.network_engine.session_manager.SetSessionDirty( network_context )
|
|
|
|
|
|
if HG.client_controller.new_options.GetBoolean( 'notify_client_api_cookies' ) and len( domains_cleared ) + len( domains_set ) > 0:
|
|
|
|
domains_cleared = sorted( domains_cleared )
|
|
domains_set = sorted( domains_set )
|
|
|
|
message = 'Cookies sent from API:'
|
|
|
|
if len( domains_cleared ) > 0:
|
|
|
|
message = '{} ({} cleared)'.format( message, ', '.join( domains_cleared ) )
|
|
|
|
|
|
if len( domains_set ) > 0:
|
|
|
|
message = '{} ({} set)'.format( message, ', '.join( domains_set ) )
|
|
|
|
|
|
job_key = ClientThreading.JobKey()
|
|
|
|
job_key.SetVariable( 'popup_text_1', message )
|
|
|
|
job_key.Delete( 5 )
|
|
|
|
HG.client_controller.pub( 'message', job_key )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManageCookiesSetUserAgent( HydrusResourceClientAPIRestrictedManageCookies ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
user_agent = request.parsed_request_args.GetValue( 'user-agent', str )
|
|
|
|
if user_agent == '':
|
|
|
|
from hydrus.client import ClientDefaults
|
|
|
|
user_agent = ClientDefaults.DEFAULT_USER_AGENT
|
|
|
|
|
|
HG.client_controller.network_engine.domain_manager.SetGlobalUserAgent( user_agent )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManageDatabase( HydrusResourceClientAPIRestricted ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_MANAGE_DATABASE )
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManageDatabaseLockOff( HydrusResourceClientAPIRestrictedManageDatabase ):
|
|
|
|
BLOCKED_WHEN_BUSY = False
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
try:
|
|
|
|
HG.client_busy.release()
|
|
|
|
except threading.ThreadError:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'The server is not busy!' )
|
|
|
|
|
|
HG.client_controller.db.PauseAndDisconnect( False )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManageDatabaseLockOn( HydrusResourceClientAPIRestrictedManageDatabase ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
locked = HG.client_busy.acquire( False ) # pylint: disable=E1111
|
|
|
|
if not locked:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'The client was already locked!' )
|
|
|
|
|
|
HG.client_controller.db.PauseAndDisconnect( True )
|
|
|
|
TIME_BLOCK = 0.25
|
|
|
|
for i in range( int( 5 / TIME_BLOCK ) ):
|
|
|
|
if not HG.client_controller.db.IsConnected():
|
|
|
|
break
|
|
|
|
|
|
time.sleep( TIME_BLOCK )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManageDatabaseMrBones( HydrusResourceClientAPIRestrictedManageDatabase ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
boned_stats = HG.client_controller.Read( 'boned_stats' )
|
|
|
|
body_dict = { 'boned_stats' : boned_stats }
|
|
|
|
mime = request.preferred_mime
|
|
body = Dumps( body_dict, mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManagePages( HydrusResourceClientAPIRestricted ):
|
|
|
|
def _CheckAPIPermissions( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
request.client_api_permissions.CheckPermission( ClientAPI.CLIENT_API_PERMISSION_MANAGE_PAGES )
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManagePagesAddFiles( HydrusResourceClientAPIRestrictedManagePages ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
def do_it( page_key, media_results ):
|
|
|
|
page = HG.client_controller.gui.GetPageFromPageKey( page_key )
|
|
|
|
from hydrus.client.gui.pages import ClientGUIPages
|
|
|
|
if page is None:
|
|
|
|
raise HydrusExceptions.DataMissing()
|
|
|
|
|
|
if not isinstance( page, ClientGUIPages.Page ):
|
|
|
|
raise HydrusExceptions.BadRequestException( 'That page key was not for a normal media page!' )
|
|
|
|
|
|
page.AddMediaResults( media_results )
|
|
|
|
|
|
if 'page_key' not in request.parsed_request_args:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'You need a page key for this request!' )
|
|
|
|
|
|
page_key = request.parsed_request_args.GetValue( 'page_key', bytes )
|
|
|
|
if 'hash' in request.parsed_request_args:
|
|
|
|
hashes = [ request.parsed_request_args.GetValue( 'hash', bytes ) ]
|
|
|
|
CheckHashLength( hashes )
|
|
|
|
media_results = HG.client_controller.Read( 'media_results', hashes, sorted = True )
|
|
|
|
elif 'hashes' in request.parsed_request_args:
|
|
|
|
hashes = request.parsed_request_args.GetValue( 'hashes', list, expected_list_type = bytes )
|
|
|
|
CheckHashLength( hashes )
|
|
|
|
media_results = HG.client_controller.Read( 'media_results', hashes, sorted = True )
|
|
|
|
elif 'file_id' in request.parsed_request_args:
|
|
|
|
hash_ids = [ request.parsed_request_args.GetValue( 'file_id', int ) ]
|
|
|
|
media_results = HG.client_controller.Read( 'media_results_from_ids', hash_ids, sorted = True )
|
|
|
|
elif 'file_ids' in request.parsed_request_args:
|
|
|
|
hash_ids = request.parsed_request_args.GetValue( 'file_ids', list, expected_list_type = int )
|
|
|
|
media_results = HG.client_controller.Read( 'media_results_from_ids', hash_ids, sorted = True )
|
|
|
|
else:
|
|
|
|
raise HydrusExceptions.BadRequestException( 'You need hashes or hash_ids for this request!' )
|
|
|
|
|
|
try:
|
|
|
|
HG.client_controller.CallBlockingToQt( HG.client_controller.gui, do_it, page_key, media_results )
|
|
|
|
except HydrusExceptions.DataMissing as e:
|
|
|
|
raise HydrusExceptions.NotFoundException( 'Could not find that page!' )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManagePagesFocusPage( HydrusResourceClientAPIRestrictedManagePages ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
def do_it( page_key ):
|
|
|
|
return HG.client_controller.gui.ShowPage( page_key )
|
|
|
|
|
|
page_key = request.parsed_request_args.GetValue( 'page_key', bytes )
|
|
|
|
try:
|
|
|
|
HG.client_controller.CallBlockingToQt( HG.client_controller.gui, do_it, page_key )
|
|
|
|
except HydrusExceptions.DataMissing as e:
|
|
|
|
raise HydrusExceptions.NotFoundException( 'Could not find that page!' )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManagePagesGetPages( HydrusResourceClientAPIRestrictedManagePages ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
def do_it():
|
|
|
|
return HG.client_controller.gui.GetCurrentSessionPageAPIInfoDict()
|
|
|
|
|
|
page_info_dict = HG.client_controller.CallBlockingToQt( HG.client_controller.gui, do_it )
|
|
|
|
body_dict = { 'pages' : page_info_dict }
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManagePagesGetPageInfo( HydrusResourceClientAPIRestrictedManagePages ):
|
|
|
|
def _threadDoGETJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
def do_it( page_key, simple ):
|
|
|
|
return HG.client_controller.gui.GetPageAPIInfoDict( page_key, simple )
|
|
|
|
|
|
page_key = request.parsed_request_args.GetValue( 'page_key', bytes )
|
|
|
|
simple = request.parsed_request_args.GetValue( 'simple', bool, default_value = True )
|
|
|
|
page_info_dict = HG.client_controller.CallBlockingToQt( HG.client_controller.gui, do_it, page_key, simple )
|
|
|
|
if page_info_dict is None:
|
|
|
|
raise HydrusExceptions.NotFoundException( 'Did not find a page for "{}"!'.format( page_key.hex() ) )
|
|
|
|
|
|
body_dict = { 'page_info' : page_info_dict }
|
|
|
|
body = Dumps( body_dict, request.preferred_mime )
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200, mime = request.preferred_mime, body = body )
|
|
|
|
return response_context
|
|
|
|
|
|
|
|
class HydrusResourceClientAPIRestrictedManagePagesRefreshPage( HydrusResourceClientAPIRestrictedManagePages ):
|
|
|
|
def _threadDoPOSTJob( self, request: HydrusServerRequest.HydrusRequest ):
|
|
|
|
def do_it( page_key ):
|
|
|
|
return HG.client_controller.gui.RefreshPage( page_key )
|
|
|
|
|
|
page_key = request.parsed_request_args.GetValue( 'page_key', bytes )
|
|
|
|
try:
|
|
|
|
HG.client_controller.CallBlockingToQt( HG.client_controller.gui, do_it, page_key )
|
|
|
|
except HydrusExceptions.DataMissing as e:
|
|
|
|
raise HydrusExceptions.NotFoundException( 'Could not find that page!' )
|
|
|
|
|
|
response_context = HydrusServerResources.ResponseContext( 200 )
|
|
|
|
return response_context
|
|
|
|
|