diff --git a/boon_tube_daemon/main.py b/boon_tube_daemon/main.py index 8639dc1..0638528 100644 --- a/boon_tube_daemon/main.py +++ b/boon_tube_daemon/main.py @@ -179,6 +179,11 @@ def check_platforms(self): logger.info(f" ⏱ Waiting {multi_video_delay}s before posting next video...") time.sleep(multi_video_delay) self.notify_new_video(platform, video_data) + # Persist progress after each video so an interrupted batch + # resumes from the next unposted video instead of skipping + # the remainder if the process is killed mid-batch. + if hasattr(platform, 'mark_posted'): + platform.mark_posted(video_data) else: # Legacy single-video path (TikTok etc.) is_new, video_data = platform.check_for_new_video() diff --git a/boon_tube_daemon/media/youtube_videos.py b/boon_tube_daemon/media/youtube_videos.py index 8107df7..77986b2 100644 --- a/boon_tube_daemon/media/youtube_videos.py +++ b/boon_tube_daemon/media/youtube_videos.py @@ -90,6 +90,18 @@ def _save_state(self): logger.debug(f"💾 Saved YouTube state to {state_file}") except Exception as e: logger.warning("⚠ Could not save YouTube state") + + def mark_posted(self, video_data: dict): + """Advance and persist the last-posted marker after a video is posted. + + Called by the daemon after each video is successfully handled so that an + interrupted batch resumes from the next unposted video rather than + silently skipping the remainder. + """ + video_id = video_data.get('video_id') + if video_id: + self.last_video_id = video_id + self._save_state() def authenticate(self) -> bool: """Authenticate with YouTube API.""" @@ -444,9 +456,10 @@ def check_for_new_videos(self, username: Optional[str] = None) -> List[dict]: if len(new_videos) > 1: logger.info(f"📦 YouTube: {len(new_videos)} new videos detected since last check") - # Update state to newest - self.last_video_id = newest_id - self._save_state() + # NOTE: Do NOT advance last_video_id here. State is advanced per-video via + # mark_posted() after each video is successfully posted, so an interrupted + # batch (e.g. container restart) resumes from the next unposted video + # instead of silently skipping the remainder. return new_videos def _resolve_channel_id(self, username: str) -> Optional[str]: