47 lines
1.4 KiB
Python
47 lines
1.4 KiB
Python
import ctypes
|
|
import time
|
|
|
|
from blake3 import blake3
|
|
|
|
# Windows atributy pro cloud/placeholder soubory
|
|
_FILE_ATTRIBUTE_OFFLINE = 0x00001000
|
|
_FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x00040000
|
|
_FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x00400000
|
|
_CLOUD_MASK = _FILE_ATTRIBUTE_OFFLINE | _FILE_ATTRIBUTE_RECALL_ON_OPEN | _FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS
|
|
|
|
|
|
def is_cloud_placeholder(path: str) -> bool:
|
|
"""Vrátí True pokud soubor není lokálně stažený (Dropbox/OneDrive placeholder)."""
|
|
attrs = ctypes.windll.kernel32.GetFileAttributesW(path)
|
|
if attrs == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
|
|
return False
|
|
return bool(attrs & _CLOUD_MASK)
|
|
|
|
|
|
def hydrate_file(path: str, timeout: int = 120, poll: int = 3) -> bool:
|
|
"""
|
|
Přinutí Dropbox stáhnout cloud placeholder otevřením souboru.
|
|
Čeká max timeout sekund. Vrátí True pokud se soubor stáhl.
|
|
"""
|
|
try:
|
|
with open(path, "rb") as f:
|
|
f.read(1)
|
|
except OSError:
|
|
pass
|
|
|
|
deadline = time.time() + timeout
|
|
while time.time() < deadline:
|
|
if not is_cloud_placeholder(path):
|
|
return True
|
|
time.sleep(poll)
|
|
return False
|
|
|
|
|
|
def blake3_file(path, chunk_size=1024 * 1024):
|
|
"""Spočítá BLAKE3 hash souboru po blocích (bez načtení do paměti)."""
|
|
h = blake3()
|
|
with open(path, "rb") as f:
|
|
for chunk in iter(lambda: f.read(chunk_size), b""):
|
|
h.update(chunk)
|
|
return h.digest()
|