WIP progress count for files added and text indicating the file deduplication stage
This commit is contained in:
+105
-3
@@ -18,7 +18,10 @@ import tempfile
|
||||
from typing import Optional
|
||||
import hashlib
|
||||
|
||||
from file_deduplication import FileDeduplicator
|
||||
try:
|
||||
from file_deduplication import FileDeduplicator
|
||||
except ImportError:
|
||||
from .file_deduplication import FileDeduplicator
|
||||
|
||||
def get_executable_directory() -> str:
|
||||
"""Get the directory containing the executable or script."""
|
||||
@@ -244,6 +247,12 @@ class ADBManager:
|
||||
status_callback=self._update_status,
|
||||
progress_callback=self._update_progress
|
||||
)
|
||||
# Transfer progress tracking
|
||||
self.transfer_progress = {
|
||||
'current_file': 0,
|
||||
'total_files': 0,
|
||||
'files_to_transfer': 0
|
||||
}
|
||||
|
||||
def set_progress_callback(self, callback: Callable[[int], None]):
|
||||
"""Set callback function for progress updates."""
|
||||
@@ -263,6 +272,23 @@ class ADBManager:
|
||||
if self.status_callback:
|
||||
self.status_callback(message)
|
||||
|
||||
def _update_transfer_progress(self, current_file: int, total_files: int):
|
||||
"""Update transfer progress for file counting."""
|
||||
self.transfer_progress['current_file'] = current_file
|
||||
self.transfer_progress['total_files'] = total_files
|
||||
# Send progress update through status callback with special format
|
||||
progress_message = f"TRANSFER_PROGRESS:{current_file}:{total_files}"
|
||||
if self.status_callback:
|
||||
self.status_callback(progress_message)
|
||||
|
||||
def _reset_transfer_progress(self):
|
||||
"""Reset transfer progress counters."""
|
||||
self.transfer_progress = {
|
||||
'current_file': 0,
|
||||
'total_files': 0,
|
||||
'files_to_transfer': 0
|
||||
}
|
||||
|
||||
def check_local_disk_space(self) -> bool:
|
||||
"""Check if there's enough disk space for ADB download."""
|
||||
try:
|
||||
@@ -384,6 +410,9 @@ class ADBManager:
|
||||
"Warning: Transferring to root drive. Consider using a subfolder."
|
||||
)
|
||||
|
||||
# Reset transfer progress
|
||||
self._reset_transfer_progress()
|
||||
|
||||
cmd = [get_adb_binary_path(), "pull", remote_path, local_path]
|
||||
self._update_status(f"Command: adb pull '{remote_path}' '{local_path}'")
|
||||
|
||||
@@ -404,11 +433,29 @@ class ADBManager:
|
||||
last_progress = 0
|
||||
start_time = time.time()
|
||||
last_update_time = start_time
|
||||
files_transferred = 0
|
||||
|
||||
if proc.stdout:
|
||||
for line in proc.stdout:
|
||||
line_count += 1
|
||||
current_time = time.time()
|
||||
|
||||
# Check for file completion patterns in ADB output
|
||||
if ": 1 file pulled" in line or "files pulled" in line:
|
||||
# Extract number of files from the line
|
||||
if "1 file pulled" in line:
|
||||
files_transferred += 1
|
||||
else:
|
||||
# Parse "X files pulled" pattern
|
||||
import re
|
||||
match = re.search(r'(\d+) files pulled', line)
|
||||
if match:
|
||||
files_transferred = int(match.group(1))
|
||||
|
||||
# Update file transfer progress
|
||||
if self.transfer_progress['files_to_transfer'] > 0:
|
||||
self._update_transfer_progress(files_transferred, self.transfer_progress['files_to_transfer'])
|
||||
|
||||
pct = self.parse_progress(line)
|
||||
|
||||
if pct is not None:
|
||||
@@ -482,6 +529,9 @@ class ADBManager:
|
||||
"Warning: Pushing from root drive. Consider using a subfolder."
|
||||
)
|
||||
|
||||
# Reset transfer progress
|
||||
self._reset_transfer_progress()
|
||||
|
||||
cmd = [get_adb_binary_path(), "push", local_path, remote_path]
|
||||
self._update_status(f"Command: adb push '{local_path}' '{remote_path}'")
|
||||
|
||||
@@ -505,11 +555,29 @@ class ADBManager:
|
||||
last_progress = 0
|
||||
start_time = time.time()
|
||||
last_update_time = start_time
|
||||
files_transferred = 0
|
||||
|
||||
if proc.stdout:
|
||||
for line in proc.stdout:
|
||||
line_count += 1
|
||||
current_time = time.time()
|
||||
|
||||
# Check for file completion patterns in ADB output
|
||||
if ": 1 file pushed" in line or "files pushed" in line:
|
||||
# Extract number of files from the line
|
||||
if "1 file pushed" in line:
|
||||
files_transferred += 1
|
||||
else:
|
||||
# Parse "X files pushed" pattern
|
||||
import re
|
||||
match = re.search(r'(\d+) files pushed', line)
|
||||
if match:
|
||||
files_transferred = int(match.group(1))
|
||||
|
||||
# Update file transfer progress
|
||||
if self.transfer_progress['files_to_transfer'] > 0:
|
||||
self._update_transfer_progress(files_transferred, self.transfer_progress['files_to_transfer'])
|
||||
|
||||
pct = self.parse_progress(line)
|
||||
|
||||
if pct is not None:
|
||||
@@ -571,6 +639,10 @@ class ADBManager:
|
||||
"Warning: Transferring to root drive. Consider using a subfolder."
|
||||
)
|
||||
|
||||
# Set file count for single file transfer
|
||||
self.transfer_progress['files_to_transfer'] = 1
|
||||
self._update_transfer_progress(0, 1)
|
||||
|
||||
cmd = [get_adb_binary_path(), "pull", remote_file_path, local_file_path]
|
||||
|
||||
# Debug output for troubleshooting
|
||||
@@ -599,6 +671,11 @@ class ADBManager:
|
||||
for line in proc.stdout:
|
||||
line_count += 1
|
||||
current_time = time.time()
|
||||
|
||||
# Check for file completion
|
||||
if "1 file pulled" in line:
|
||||
self._update_transfer_progress(1, 1)
|
||||
|
||||
pct = self.parse_progress(line)
|
||||
|
||||
if pct is not None:
|
||||
@@ -665,6 +742,10 @@ class ADBManager:
|
||||
"Warning: Pushing from root drive. Consider using a subfolder."
|
||||
)
|
||||
|
||||
# Set file count for single file transfer
|
||||
self.transfer_progress['files_to_transfer'] = 1
|
||||
self._update_transfer_progress(0, 1)
|
||||
|
||||
cmd = [get_adb_binary_path(), "push", local_file_path, remote_file_path]
|
||||
|
||||
# Debug output for troubleshooting
|
||||
@@ -696,6 +777,11 @@ class ADBManager:
|
||||
for line in proc.stdout:
|
||||
line_count += 1
|
||||
current_time = time.time()
|
||||
|
||||
# Check for file completion
|
||||
if "1 file pushed" in line:
|
||||
self._update_transfer_progress(1, 1)
|
||||
|
||||
pct = self.parse_progress(line)
|
||||
|
||||
if pct is not None:
|
||||
@@ -784,6 +870,9 @@ class ADBManager:
|
||||
# Create local directory
|
||||
os.makedirs(local_path, exist_ok=True)
|
||||
|
||||
# Start duplicate scanning
|
||||
self._update_status("Scanning for duplicates...")
|
||||
|
||||
# Get list of remote files
|
||||
stdout, stderr, returncode = self.run_adb_command(['shell', 'find', remote_path, '-type', 'f'], capture_output=True)
|
||||
|
||||
@@ -820,10 +909,15 @@ class ADBManager:
|
||||
duplicate_files, is_remote=True, adb_command_runner=self.run_adb_command
|
||||
)
|
||||
stats['bytes_saved'] = bytes_saved
|
||||
self._update_status(f"Skipping {files_saved} duplicates, saving {self.deduplicator.format_bytes(bytes_saved)}")
|
||||
self._update_status(f"Duplicate scan complete. Skipping {files_saved} duplicates, saving {self.deduplicator.format_bytes(bytes_saved)}")
|
||||
else:
|
||||
self._update_status("Duplicate scan complete. No duplicates found.")
|
||||
|
||||
# Transfer non-duplicate files
|
||||
if files_to_transfer:
|
||||
# Set the file count for transfer progress tracking
|
||||
self.transfer_progress['files_to_transfer'] = len(files_to_transfer)
|
||||
self._update_status("Starting transfer...")
|
||||
success = self.pull_folder(remote_path, local_path)
|
||||
if success:
|
||||
stats['transferred'] = len(files_to_transfer)
|
||||
@@ -862,6 +956,9 @@ class ADBManager:
|
||||
self._update_status(f"Local path does not exist: {local_path}")
|
||||
return False, stats
|
||||
|
||||
# Start duplicate scanning
|
||||
self._update_status("Scanning for duplicates...")
|
||||
|
||||
# Get list of local files
|
||||
local_files = []
|
||||
for root, dirs, files in os.walk(local_path):
|
||||
@@ -897,10 +994,15 @@ class ADBManager:
|
||||
duplicate_files, is_remote=False
|
||||
)
|
||||
stats['bytes_saved'] = bytes_saved
|
||||
self._update_status(f"Skipping {files_saved} duplicates, saving {self.deduplicator.format_bytes(bytes_saved)}")
|
||||
self._update_status(f"Duplicate scan complete. Skipping {files_saved} duplicates, saving {self.deduplicator.format_bytes(bytes_saved)}")
|
||||
else:
|
||||
self._update_status("Duplicate scan complete. No duplicates found.")
|
||||
|
||||
# Transfer non-duplicate files
|
||||
if files_to_transfer:
|
||||
# Set the file count for transfer progress tracking
|
||||
self.transfer_progress['files_to_transfer'] = len(files_to_transfer)
|
||||
self._update_status("Starting transfer...")
|
||||
success = self.push_folder(local_path, remote_path)
|
||||
if success:
|
||||
stats['transferred'] = len(files_to_transfer)
|
||||
|
||||
+103
-8
@@ -237,6 +237,13 @@ class AndroidFileHandlerGUI(tk.Tk):
|
||||
self.transfer_animation_job = None
|
||||
self.transfer_dots = 0
|
||||
|
||||
# File transfer progress tracking
|
||||
self.transfer_file_progress = {
|
||||
'current': 0,
|
||||
'total': 0,
|
||||
'active': False
|
||||
}
|
||||
|
||||
# Set up ADB callbacks to our own methods
|
||||
self.adb_manager.set_progress_callback(self._update_progress)
|
||||
self.adb_manager.set_status_callback(self._update_status)
|
||||
@@ -253,11 +260,50 @@ class AndroidFileHandlerGUI(tk.Tk):
|
||||
|
||||
def _update_status(self, message: str):
|
||||
"""Update status label (thread-safe)."""
|
||||
# Don't update status if transfer animation is running
|
||||
if self.transfer_animation_job is not None:
|
||||
return # Ignore ADB status updates during transfer animation
|
||||
|
||||
def update_ui():
|
||||
# Check for transfer progress updates
|
||||
if message.startswith("TRANSFER_PROGRESS:"):
|
||||
parts = message.split(":")
|
||||
if len(parts) == 3:
|
||||
current = int(parts[1])
|
||||
total = int(parts[2])
|
||||
self.transfer_file_progress['current'] = current
|
||||
self.transfer_file_progress['total'] = total
|
||||
self.transfer_file_progress['active'] = True
|
||||
# Don't update the UI here - let the animation handle it
|
||||
return
|
||||
|
||||
# Check if we should start or switch animations based on message content
|
||||
if self.transfer_animation_job is not None:
|
||||
# Animation is already running
|
||||
if "Scanning for duplicates" in message and not hasattr(self, '_scanning_active'):
|
||||
# Already scanning, ignore duplicate "Scanning for duplicates..." messages
|
||||
return
|
||||
elif "Starting transfer" in message or "Transferring" in message:
|
||||
# Switch from scanning to transfer animation
|
||||
if hasattr(self, '_scanning_active'):
|
||||
delattr(self, '_scanning_active')
|
||||
self._stop_transfer_animation()
|
||||
self._start_transfer_animation()
|
||||
return
|
||||
elif ("Duplicate scan complete" in message or
|
||||
"No duplicates found" in message or
|
||||
"All files already exist" in message):
|
||||
# Allow these messages to show briefly before transfer starts
|
||||
self._stop_transfer_animation()
|
||||
self.status_label.config(text=message)
|
||||
self.update_idletasks()
|
||||
return
|
||||
else:
|
||||
# Don't update status if transfer animation is running (except for specific cases)
|
||||
return
|
||||
elif "Scanning for duplicates" in message:
|
||||
# Start scanning animation
|
||||
self._scanning_active = True
|
||||
self._start_scanning_animation()
|
||||
return
|
||||
|
||||
# Normal status update
|
||||
self.status_label.config(text=message)
|
||||
self.update_idletasks()
|
||||
|
||||
@@ -313,11 +359,37 @@ class AndroidFileHandlerGUI(tk.Tk):
|
||||
self.transfer_animation_job = True # Mark as active before starting
|
||||
self._animate_transfer_text()
|
||||
|
||||
def _start_scanning_animation(self):
|
||||
"""Start the 'Scanning for duplicates...' animation."""
|
||||
self.transfer_dots = 0
|
||||
self.transfer_animation_job = True # Mark as active before starting
|
||||
self._animate_scanning_text()
|
||||
|
||||
def _animate_scanning_text(self):
|
||||
"""Animate the scanning text with dots."""
|
||||
if self.transfer_animation_job is not None:
|
||||
dots = "." * (self.transfer_dots + 1)
|
||||
status_text = f"Scanning for duplicates{dots}"
|
||||
self.status_label.config(text=status_text)
|
||||
self.update_idletasks() # Force immediate UI update
|
||||
self.transfer_dots = (self.transfer_dots + 1) % 5 # Cycle 0-4 dots
|
||||
# Schedule next update in 500ms
|
||||
self.transfer_animation_job = self.after(500, self._animate_scanning_text)
|
||||
|
||||
def _animate_transfer_text(self):
|
||||
"""Animate the transfer text with dots."""
|
||||
if self.transfer_animation_job is not None:
|
||||
dots = "." * (self.transfer_dots + 1)
|
||||
status_text = f"Transferring{dots}"
|
||||
|
||||
# Show file progress if available
|
||||
if (self.transfer_file_progress['active'] and
|
||||
self.transfer_file_progress['total'] > 0):
|
||||
current = self.transfer_file_progress['current']
|
||||
total = self.transfer_file_progress['total']
|
||||
status_text = f"Transferring {current} of {total} files{dots}"
|
||||
else:
|
||||
status_text = f"Transferring{dots}"
|
||||
|
||||
self.status_label.config(text=status_text)
|
||||
self.update_idletasks() # Force immediate UI update
|
||||
self.transfer_dots = (self.transfer_dots + 1) % 5 # Cycle 0-4 dots
|
||||
@@ -330,6 +402,17 @@ class AndroidFileHandlerGUI(tk.Tk):
|
||||
if isinstance(self.transfer_animation_job, str): # It's an after job ID
|
||||
self.after_cancel(self.transfer_animation_job)
|
||||
self.transfer_animation_job = None
|
||||
|
||||
# Clear scanning flag if it exists
|
||||
if hasattr(self, '_scanning_active'):
|
||||
delattr(self, '_scanning_active')
|
||||
|
||||
# Reset file progress tracking
|
||||
self.transfer_file_progress = {
|
||||
'current': 0,
|
||||
'total': 0,
|
||||
'active': False
|
||||
}
|
||||
|
||||
def _initialize_app(self):
|
||||
"""Initialize the application - check ADB and device."""
|
||||
@@ -781,13 +864,19 @@ class AndroidFileHandlerGUI(tk.Tk):
|
||||
self.disable_controls()
|
||||
# Switch button to cancel mode during transfer
|
||||
self._switch_to_cancel_mode()
|
||||
# Start the transfer animation
|
||||
self._start_transfer_animation()
|
||||
|
||||
# Start with scanning animation for folder transfers
|
||||
# File transfers will skip directly to transfer animation
|
||||
|
||||
# Determine if we're dealing with files or folders
|
||||
if direction == "pull":
|
||||
# For pull, check if remote path is a file
|
||||
is_file = self._is_remote_file(remote_path)
|
||||
if not is_file:
|
||||
# Start scanning animation for folder transfers
|
||||
self._start_scanning_animation()
|
||||
else:
|
||||
# Start transfer animation for file transfers
|
||||
self._start_transfer_animation()
|
||||
threading.Thread(
|
||||
target=self._transfer_thread,
|
||||
args=(direction, remote_path, local_path, transfer_id, is_file),
|
||||
@@ -796,6 +885,12 @@ class AndroidFileHandlerGUI(tk.Tk):
|
||||
elif direction == "push":
|
||||
# For push, check if local path is a file
|
||||
is_file = os.path.isfile(local_path)
|
||||
if not is_file:
|
||||
# Start scanning animation for folder transfers
|
||||
self._start_scanning_animation()
|
||||
else:
|
||||
# Start transfer animation for file transfers
|
||||
self._start_transfer_animation()
|
||||
threading.Thread(
|
||||
target=self._transfer_thread,
|
||||
args=(direction, local_path, remote_path, transfer_id, is_file),
|
||||
|
||||
Reference in New Issue
Block a user