383 lines
9.0 KiB
Python
383 lines
9.0 KiB
Python
import gc
|
|
import HydrusConstants as HC
|
|
import HydrusData
|
|
import os
|
|
import psutil
|
|
import send2trash
|
|
import shutil
|
|
import stat
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import threading
|
|
import traceback
|
|
|
|
def AppendPathUntilNoConflicts( path ):
|
|
|
|
( path_absent_ext, ext ) = os.path.splitext( path )
|
|
|
|
good_path_absent_ext = path_absent_ext
|
|
|
|
i = 0
|
|
|
|
while os.path.exists( good_path_absent_ext + ext ):
|
|
|
|
good_path_absent_ext = path_absent_ext + '_' + str( i )
|
|
|
|
i += 1
|
|
|
|
|
|
return good_path_absent_ext + ext
|
|
|
|
def CleanUpTempPath( os_file_handle, temp_path ):
|
|
|
|
try:
|
|
|
|
os.close( os_file_handle )
|
|
|
|
except OSError:
|
|
|
|
gc.collect()
|
|
|
|
try:
|
|
|
|
os.close( os_file_handle )
|
|
|
|
except OSError:
|
|
|
|
HydrusData.Print( 'Could not close the temporary file ' + temp_path )
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
|
|
|
os.remove( temp_path )
|
|
|
|
except OSError:
|
|
|
|
gc.collect()
|
|
|
|
try:
|
|
|
|
os.remove( temp_path )
|
|
|
|
except OSError:
|
|
|
|
HydrusData.Print( 'Could not delete the temporary file ' + temp_path )
|
|
|
|
|
|
|
|
def ConvertAbsPathToPortablePath( abs_path ):
|
|
|
|
try:
|
|
|
|
portable_path = os.path.relpath( abs_path, HC.BASE_DIR )
|
|
|
|
except:
|
|
|
|
portable_path = abs_path
|
|
|
|
|
|
if HC.PLATFORM_WINDOWS:
|
|
|
|
portable_path = portable_path.replace( '\\', '/' ) # store seps as /, to maintain multiplatform uniformity
|
|
|
|
|
|
return portable_path
|
|
|
|
def ConvertPortablePathToAbsPath( portable_path ):
|
|
|
|
portable_path = os.path.normpath( portable_path ) # collapses .. stuff and converts / to \\ for windows only
|
|
|
|
if os.path.isabs( portable_path ):
|
|
|
|
abs_path = portable_path
|
|
|
|
else:
|
|
|
|
abs_path = os.path.join( HC.BASE_DIR, portable_path )
|
|
|
|
|
|
if not HC.PLATFORM_WINDOWS and not os.path.exists( abs_path ):
|
|
|
|
abs_path = abs_path.replace( '\\', '/' )
|
|
|
|
|
|
return abs_path
|
|
|
|
def CopyAndMergeTree( source, dest ):
|
|
|
|
pauser = HydrusData.BigJobPauser()
|
|
|
|
if not os.path.exists( dest ):
|
|
|
|
os.makedirs( dest )
|
|
|
|
|
|
for ( root, dirnames, filenames ) in os.walk( source ):
|
|
|
|
dest_root = root.replace( source, dest )
|
|
|
|
for dirname in dirnames:
|
|
|
|
pauser.Pause()
|
|
|
|
source_path = os.path.join( root, dirname )
|
|
dest_path = os.path.join( dest_root, dirname )
|
|
|
|
if not os.path.exists( dest_path ):
|
|
|
|
os.makedirs( dest_path )
|
|
|
|
|
|
shutil.copystat( source_path, dest_path )
|
|
|
|
|
|
for filename in filenames:
|
|
|
|
pauser.Pause()
|
|
|
|
source_path = os.path.join( root, filename )
|
|
dest_path = os.path.join( dest_root, filename )
|
|
|
|
shutil.copy2( source_path, dest_path )
|
|
|
|
|
|
|
|
def CopyFileLikeToFileLike( f_source, f_dest ):
|
|
|
|
for block in ReadFileLikeAsBlocks( f_source ): f_dest.write( block )
|
|
|
|
def DeletePath( path ):
|
|
|
|
if os.path.exists( path ):
|
|
|
|
MakeFileWritable( path )
|
|
|
|
if os.path.isdir( path ):
|
|
|
|
shutil.rmtree( path )
|
|
|
|
else:
|
|
|
|
os.remove( path )
|
|
|
|
|
|
|
|
def GetDevice( path ):
|
|
|
|
path = path.lower()
|
|
|
|
partition_infos = psutil.disk_partitions()
|
|
|
|
def sort_descending_mountpoint( partition_info ): # i.e. put '/home' before '/'
|
|
|
|
return - len( partition_info.mountpoint )
|
|
|
|
|
|
partition_infos.sort( key = sort_descending_mountpoint )
|
|
|
|
for partition_info in partition_infos:
|
|
|
|
if path.startswith( partition_info.mountpoint.lower() ):
|
|
|
|
return partition_info.device
|
|
|
|
|
|
|
|
return None
|
|
|
|
def GetTempFile(): return tempfile.TemporaryFile()
|
|
def GetTempFileQuick(): return tempfile.SpooledTemporaryFile( max_size = 1024 * 1024 * 4 )
|
|
def GetTempPath(): return tempfile.mkstemp( prefix = 'hydrus' )
|
|
|
|
def LaunchDirectory( path ):
|
|
|
|
def do_it():
|
|
|
|
if HC.PLATFORM_WINDOWS:
|
|
|
|
os.startfile( path )
|
|
|
|
else:
|
|
|
|
if HC.PLATFORM_OSX: cmd = [ 'open' ]
|
|
elif HC.PLATFORM_LINUX: cmd = [ 'xdg-open' ]
|
|
|
|
cmd.append( path )
|
|
|
|
process = subprocess.Popen( cmd, startupinfo = HydrusData.GetSubprocessStartupInfo() )
|
|
|
|
process.wait()
|
|
|
|
process.communicate()
|
|
|
|
|
|
|
|
thread = threading.Thread( target = do_it )
|
|
|
|
thread.daemon = True
|
|
|
|
thread.start()
|
|
|
|
def LaunchFile( path ):
|
|
|
|
def do_it():
|
|
|
|
if HC.PLATFORM_WINDOWS:
|
|
|
|
os.startfile( path )
|
|
|
|
else:
|
|
|
|
if HC.PLATFORM_OSX: cmd = [ 'open' ]
|
|
elif HC.PLATFORM_LINUX: cmd = [ 'xdg-open' ]
|
|
|
|
cmd.append( path )
|
|
|
|
process = subprocess.Popen( cmd, startupinfo = HydrusData.GetSubprocessStartupInfo() )
|
|
|
|
process.wait()
|
|
|
|
process.communicate()
|
|
|
|
|
|
|
|
thread = threading.Thread( target = do_it )
|
|
|
|
thread.daemon = True
|
|
|
|
thread.start()
|
|
|
|
def MakeFileWritable( path ):
|
|
|
|
try: os.chmod( dest_path, stat.S_IWRITE | stat.S_IREAD )
|
|
except: pass
|
|
|
|
def MirrorTree( source, dest ):
|
|
|
|
pauser = HydrusData.BigJobPauser()
|
|
|
|
if not os.path.exists( dest ):
|
|
|
|
os.makedirs( dest )
|
|
|
|
|
|
for ( root, dirnames, filenames ) in os.walk( source ):
|
|
|
|
dest_root = root.replace( source, dest )
|
|
|
|
surplus_dest_paths = { os.path.join( dest_root, dest_filename ) for dest_filename in os.listdir( dest_root ) }
|
|
|
|
for dirname in dirnames:
|
|
|
|
pauser.Pause()
|
|
|
|
source_path = os.path.join( root, dirname )
|
|
dest_path = os.path.join( dest_root, dirname )
|
|
|
|
surplus_dest_paths.discard( dest_path )
|
|
|
|
if not os.path.exists( dest_path ):
|
|
|
|
os.makedirs( dest_path )
|
|
|
|
|
|
shutil.copystat( source_path, dest_path )
|
|
|
|
|
|
for filename in filenames:
|
|
|
|
pauser.Pause()
|
|
|
|
source_path = os.path.join( root, filename )
|
|
|
|
dest_path = os.path.join( dest_root, filename )
|
|
|
|
surplus_dest_paths.discard( dest_path )
|
|
|
|
if not PathsHaveSameSizeAndDate( source_path, dest_path ):
|
|
|
|
shutil.copy2( source_path, dest_path )
|
|
|
|
|
|
|
|
for dest_path in surplus_dest_paths:
|
|
|
|
pauser.Pause()
|
|
|
|
DeletePath( dest_path )
|
|
|
|
|
|
|
|
def PathsHaveSameSizeAndDate( path1, path2 ):
|
|
|
|
if os.path.exists( path1 ) and os.path.exists( path2 ):
|
|
|
|
same_size = os.path.getsize( path1 ) == os.path.getsize( path2 )
|
|
same_modified_time = os.path.getmtime( path1 ) == os.path.getmtime( path2 )
|
|
|
|
if same_size and same_modified_time:
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
def ReadFileLikeAsBlocks( f ):
|
|
|
|
next_block = f.read( HC.READ_BLOCK_SIZE )
|
|
|
|
while next_block != '':
|
|
|
|
yield next_block
|
|
|
|
next_block = f.read( HC.READ_BLOCK_SIZE )
|
|
|
|
|
|
def RecyclePath( path ):
|
|
|
|
original_path = path
|
|
|
|
if HC.PLATFORM_LINUX:
|
|
|
|
# send2trash for Linux tries to do some Python3 str() stuff in prepping non-str paths for recycling
|
|
|
|
if not isinstance( path, str ):
|
|
|
|
try:
|
|
|
|
path = path.encode( sys.getfilesystemencoding() )
|
|
|
|
except:
|
|
|
|
HydrusData.Print( 'Trying to prepare a file for recycling created this error:' )
|
|
traceback.print_exc()
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if os.path.exists( path ):
|
|
|
|
MakeFileWritable( path )
|
|
|
|
try:
|
|
|
|
send2trash.send2trash( path )
|
|
|
|
except:
|
|
|
|
HydrusData.Print( 'Trying to recycle a file created this error:' )
|
|
traceback.print_exc()
|
|
|
|
HydrusData.Print( 'It has been fully deleted instead.' )
|
|
|
|
DeletePath( original_path )
|
|
|
|
|
|
|