WIP progress count for files added and text indicating the file deduplication stage

This commit is contained in:
2025-09-23 06:01:53 +08:00
committed by Jason Ross
parent 33f19f9058
commit 21e858dfca
2 changed files with 208 additions and 11 deletions
+105 -3
View File
@@ -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
View File
@@ -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),