diff --git a/src/app/core/download/downloader.py b/src/app/core/download/downloader.py index 6cd62f2..82ad039 100644 --- a/src/app/core/download/downloader.py +++ b/src/app/core/download/downloader.py @@ -22,6 +22,12 @@ class Downloader: # Optional local audio extraction when requested if job.mode == "video" and job.audio_output_path is not None: await self._extract_audio(job) + # Remove the video if requested (audio-only mode) + if not job.keep_video and job.output_path: + try: + job.output_path.unlink(missing_ok=True) + except Exception: + pass job.state = JobState.COMPLETED except Exception as exc: # pragma: no cover - environment dependent job.state = JobState.FAILED diff --git a/src/app/core/download/queue_manager.py b/src/app/core/download/queue_manager.py index c465b72..da83cd1 100644 --- a/src/app/core/download/queue_manager.py +++ b/src/app/core/download/queue_manager.py @@ -29,6 +29,7 @@ class DownloadJob: error: Optional[str] = None ffmpeg_path: Optional[str] = None audio_output_path: Optional[Path] = None # when mode=video and we also want mp3 + keep_video: bool = True class QueueManager: diff --git a/src/app/core/sync/executor.py b/src/app/core/sync/executor.py index 19d231c..c602340 100644 --- a/src/app/core/sync/executor.py +++ b/src/app/core/sync/executor.py @@ -130,6 +130,8 @@ class ActionExecutor: d["video"] = a.to_name ffmpeg_cfg = str(playlist_cfg.get("ffmpeg_path", "ffmpeg")) if playlist_cfg.get("ffmpeg_path") is not None else None + temp_video_root = video_root / ".tmp" + temp_video_root.mkdir(parents=True, exist_ok=True) for a in actions: if a.type != SyncActionType.DOWNLOAD or not a.item or not a.to_name: @@ -161,19 +163,39 @@ class ActionExecutor: # Normal single-output path is_audio = a.to_name.endswith(".mp3") - root = audio_root if is_audio else video_root - output_path = root / a.to_name - output_path.parent.mkdir(parents=True, exist_ok=True) url = f"https://www.youtube.com/watch?v={vid}" - job = DownloadJob( - item=a.item, - output_path=output_path, - url=url, - mode=("audio" if is_audio else "video"), - ffmpeg_path=ffmpeg_cfg, - ) - jobs.append(job) - await queue.enqueue(job) + + if is_audio: + # Audio-only: download video to temp, extract mp3, then delete video + audio_path = audio_root / a.to_name + audio_path.parent.mkdir(parents=True, exist_ok=True) + # build temp video filename from audio base + temp_base = a.to_name.rsplit(".", 1)[0] + ".mp4" + video_temp = temp_video_root / temp_base + job = DownloadJob( + item=a.item, + output_path=video_temp, + url=url, + mode="video", + ffmpeg_path=ffmpeg_cfg, + audio_output_path=audio_path, + keep_video=False, + ) + jobs.append(job) + await queue.enqueue(job) + else: + # Video-only + video_path = video_root / a.to_name + video_path.parent.mkdir(parents=True, exist_ok=True) + job = DownloadJob( + item=a.item, + output_path=video_path, + url=url, + mode="video", + ffmpeg_path=ffmpeg_cfg, + ) + jobs.append(job) + await queue.enqueue(job) finally: await queue._queue.join() # wait for all jobs await queue.stop()