Scope cleanup routines to files created by the current download
cleanup_partial_files() deleted every *.part and *.fNNN.* file in the whole download directory, and cleanup_subtitle_files() deleted any new .vtt/.srt under it recursively - including files belonging to other applications (e.g. a browser's own .part downloads in ~/Downloads). Track every destination path yt-dlp reports for this download and restrict both cleanup passes to those files and their .part/.ytdl siblings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -112,18 +112,23 @@ class DownloadThread(QThread):
|
||||
self.last_file_path: Optional[str] = None # Initialize full file path storage
|
||||
self.subtitle_files: List[str] = [] # Track subtitle files that are created
|
||||
self.initial_subtitle_files: Set[Path] = set() # Track initial subtitle files before download
|
||||
self.download_files: Set[Path] = set() # Every destination path this download wrote to
|
||||
|
||||
def cleanup_partial_files(self) -> None:
|
||||
"""Delete any partial files including .part and unmerged format-specific files"""
|
||||
"""Delete partial files (.part/.ytdl and unmerged .fNNN. streams), but only
|
||||
those belonging to destinations this download actually wrote — the download
|
||||
directory may contain unrelated files from other applications."""
|
||||
try:
|
||||
pattern = re.compile(r"\.f\d+\.") # Pattern to match format codes like .f243.
|
||||
for file_path in self.path.iterdir():
|
||||
if file_path.suffix == ".part" or pattern.search(file_path.name):
|
||||
self._safe_delete_with_retry(file_path)
|
||||
for dest in self.download_files:
|
||||
candidates = [dest.with_name(dest.name + ".part"), dest.with_name(dest.name + ".ytdl")]
|
||||
if dest.suffix == ".part" or pattern.search(dest.name):
|
||||
candidates.append(dest)
|
||||
for file_path in candidates:
|
||||
if file_path.exists():
|
||||
self._safe_delete_with_retry(file_path)
|
||||
except Exception as e:
|
||||
logger.exception(f"Error cleaning partial files: {e}")
|
||||
# Don't emit error signal for cleanup issues to avoid crashing the thread
|
||||
logger.error(f"Error cleaning partial files: {e}")
|
||||
|
||||
def _safe_delete_with_retry(self, file_path: Path, max_retries: int = 5, delay: float = 2.0) -> None:
|
||||
"""Safely delete a file with retry mechanism for file locking issues across platforms"""
|
||||
@@ -219,8 +224,15 @@ class DownloadThread(QThread):
|
||||
logger.debug(f"Deleted {deleted_count[0]} of {len(self.subtitle_files)} tracked subtitle files")
|
||||
|
||||
# --- Method 2: Delete new subtitle files not in initial set ---
|
||||
# Only touch subtitles belonging to files this download wrote; the
|
||||
# directory may contain subtitle files from other processes.
|
||||
download_stems = {p.stem for p in self.download_files} | {Path(f).stem for f in self.subtitle_files or []}
|
||||
new_subtitle_files: Set[Path] = {
|
||||
f for f in Path(self.path).rglob("*") if f.suffix in [".vtt", ".srt"] and f not in self.initial_subtitle_files
|
||||
f
|
||||
for f in Path(self.path).rglob("*")
|
||||
if f.suffix in [".vtt", ".srt"]
|
||||
and f not in self.initial_subtitle_files
|
||||
and any(f.name.startswith(stem) for stem in download_stems if stem)
|
||||
}
|
||||
for subtitle_file in new_subtitle_files:
|
||||
deleted_count[1] += safe_delete(path=subtitle_file)
|
||||
@@ -619,6 +631,7 @@ class DownloadThread(QThread):
|
||||
filepath = dest_match.group(1).strip()
|
||||
self.current_filename = Path(filepath).name
|
||||
self.last_file_path = filepath # Store the full path for later cleanup
|
||||
self.download_files.add(Path(filepath))
|
||||
logger.debug(f"Extracted filename: {self.current_filename}") # DEBUG
|
||||
|
||||
# Check if this is an audio-only download by looking in the previous lines
|
||||
@@ -711,6 +724,7 @@ class DownloadThread(QThread):
|
||||
dest_path = match.group(1).strip()
|
||||
self.current_filename = Path(dest_path).name
|
||||
self.last_file_path = dest_path
|
||||
self.download_files.add(Path(dest_path))
|
||||
logger.debug(f"Captured destination filename: {self.current_filename}")
|
||||
elif "Downloading API JSON" in line:
|
||||
self.status_signal.emit(_("download.processing_playlist"))
|
||||
@@ -771,6 +785,7 @@ class DownloadThread(QThread):
|
||||
merged_filepath = merger_match.group(1).strip()
|
||||
self.current_filename = Path(merged_filepath).name
|
||||
self.last_file_path = merged_filepath
|
||||
self.download_files.add(Path(merged_filepath))
|
||||
logger.debug(f"Updated to merged filename: {self.current_filename}")
|
||||
elif "SponsorBlock" in line:
|
||||
self.status_signal.emit(_("download.removing_sponsor_segments"))
|
||||
|
||||
Reference in New Issue
Block a user