|
|
|
@ -830,3 +830,99 @@ def get_cachedir(params={}):
|
|
|
|
|
cache_root = os.environ.get('XDG_CACHE_HOME',
|
|
|
|
|
os.path.expanduser('~/.cache'))
|
|
|
|
|
return params.get('cachedir', os.path.join(cache_root, 'youtube-dl'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Cross-platform file locking
|
|
|
|
|
if sys.platform == 'win32':
|
|
|
|
|
import ctypes.wintypes
|
|
|
|
|
import msvcrt
|
|
|
|
|
|
|
|
|
|
class OVERLAPPED(ctypes.Structure):
|
|
|
|
|
_fields_ = [
|
|
|
|
|
('Internal', ctypes.wintypes.LPVOID),
|
|
|
|
|
('InternalHigh', ctypes.wintypes.LPVOID),
|
|
|
|
|
('Offset', ctypes.wintypes.DWORD),
|
|
|
|
|
('OffsetHigh', ctypes.wintypes.DWORD),
|
|
|
|
|
('hEvent', ctypes.wintypes.HANDLE),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
kernel32 = ctypes.windll.kernel32
|
|
|
|
|
LockFileEx = kernel32.LockFileEx
|
|
|
|
|
LockFileEx.argtypes = [
|
|
|
|
|
ctypes.wintypes.HANDLE, # hFile
|
|
|
|
|
ctypes.wintypes.DWORD, # dwFlags
|
|
|
|
|
ctypes.wintypes.DWORD, # dwReserved
|
|
|
|
|
ctypes.wintypes.DWORD, # nNumberOfBytesToLockLow
|
|
|
|
|
ctypes.wintypes.DWORD, # nNumberOfBytesToLockHigh
|
|
|
|
|
ctypes.POINTER(OVERLAPPED) # Overlapped
|
|
|
|
|
]
|
|
|
|
|
LockFileEx.restype = ctypes.wintypes.BOOL
|
|
|
|
|
UnlockFileEx = kernel32.UnlockFileEx
|
|
|
|
|
UnlockFileEx.argtypes = [
|
|
|
|
|
ctypes.wintypes.HANDLE, # hFile
|
|
|
|
|
ctypes.wintypes.DWORD, # dwReserved
|
|
|
|
|
ctypes.wintypes.DWORD, # nNumberOfBytesToLockLow
|
|
|
|
|
ctypes.wintypes.DWORD, # nNumberOfBytesToLockHigh
|
|
|
|
|
ctypes.POINTER(OVERLAPPED) # Overlapped
|
|
|
|
|
]
|
|
|
|
|
UnlockFileEx.restype = ctypes.wintypes.BOOL
|
|
|
|
|
whole_low = 0xffffffff
|
|
|
|
|
whole_high = 0x7fffffff
|
|
|
|
|
|
|
|
|
|
def _lock_file(f, exclusive):
|
|
|
|
|
overlapped = OVERLAPPED()
|
|
|
|
|
overlapped.Offset = 0
|
|
|
|
|
overlapped.OffsetHigh = 0
|
|
|
|
|
overlapped.hEvent = 0
|
|
|
|
|
f._lock_file_overlapped_p = ctypes.pointer(overlapped)
|
|
|
|
|
handle = msvcrt.get_osfhandle(f.fileno())
|
|
|
|
|
if not LockFileEx(handle, 0x2 if exclusive else 0x0, 0,
|
|
|
|
|
whole_low, whole_high, f._lock_file_overlapped_p):
|
|
|
|
|
raise OSError('Locking file failed: %r' % ctypes.FormatError())
|
|
|
|
|
|
|
|
|
|
def _unlock_file(f):
|
|
|
|
|
assert f._lock_file_overlapped_p
|
|
|
|
|
handle = msvcrt.get_osfhandle(f.fileno())
|
|
|
|
|
if not UnlockFileEx(handle, 0,
|
|
|
|
|
whole_low, whole_high, f._lock_file_overlapped_p):
|
|
|
|
|
raise OSError('Unlocking file failed: %r' % ctypes.FormatError())
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
import fcntl
|
|
|
|
|
|
|
|
|
|
def _lock_file(f, exclusive):
|
|
|
|
|
fcntl.lockf(f, fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH)
|
|
|
|
|
|
|
|
|
|
def _unlock_file(f):
|
|
|
|
|
fcntl.lockf(f, fcntl.LOCK_UN)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class locked_file(object):
|
|
|
|
|
def __init__(self, filename, mode, encoding=None):
|
|
|
|
|
assert mode in ['r', 'a', 'w']
|
|
|
|
|
self.f = io.open(filename, mode, encoding=encoding)
|
|
|
|
|
self.mode = mode
|
|
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
|
exclusive = self.mode != 'r'
|
|
|
|
|
try:
|
|
|
|
|
_lock_file(self.f, exclusive)
|
|
|
|
|
except IOError:
|
|
|
|
|
self.f.close()
|
|
|
|
|
raise
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def __exit__(self, etype, value, traceback):
|
|
|
|
|
try:
|
|
|
|
|
_unlock_file(self.f)
|
|
|
|
|
finally:
|
|
|
|
|
self.f.close()
|
|
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
|
return iter(self.f)
|
|
|
|
|
|
|
|
|
|
def write(self, *args):
|
|
|
|
|
return self.f.write(*args)
|
|
|
|
|
|
|
|
|
|
def read(self, *args):
|
|
|
|
|
return self.f.read(*args)
|
|
|
|
|