(self)
| 856 | return max(0.0, min(self._scheduled_probe_at.values()) - time.time()) |
| 857 | |
| 858 | async def _live_monitor_loop(self) -> None: |
| 859 | while True: |
| 860 | timeout = self._seconds_until_next_probe() |
| 861 | try: |
| 862 | await asyncio.wait_for(self._live_monitor_wakeup.wait(), timeout=timeout) |
| 863 | except asyncio.TimeoutError: |
| 864 | pass |
| 865 | self._live_monitor_wakeup.clear() |
| 866 | now = time.time() |
| 867 | due: list[Download] = [] |
| 868 | for url, probe_at in list(self._scheduled_probe_at.items()): |
| 869 | if now < probe_at: |
| 870 | continue |
| 871 | if not self.queue.exists(url): |
| 872 | self._unregister_scheduled(url) |
| 873 | continue |
| 874 | download = self.queue.get(url) |
| 875 | if download.info.status != 'scheduled' or download.canceled: |
| 876 | self._unregister_scheduled(url) |
| 877 | continue |
| 878 | due.append(download) |
| 879 | for download in due: |
| 880 | try: |
| 881 | await self._probe_scheduled_download(download) |
| 882 | except Exception as exc: |
| 883 | # Defensive: _probe_scheduled_download handles its own errors, |
| 884 | # but never let an unexpected failure leave probe_at in the past |
| 885 | # (which would spin this loop) or kill the monitor task. |
| 886 | log.exception("Scheduled live probe crashed for %s: %s", download.info.url, exc) |
| 887 | if download.info.url in self._scheduled_probe_at: |
| 888 | self._scheduled_probe_at[download.info.url] = time.time() + _LIVE_CHECK_INTERVAL |
| 889 | |
| 890 | async def _probe_scheduled_download(self, download: Download) -> None: |
| 891 | url = download.info.url |
no test coverage detected