Pause discovery metadata calls while a download is active
_meta_run now checks _active_downloads before each background yt-dlp call. If a download is running it waits (3s poll loop) until the download finishes before making the next metadata request. This prevents YouTube from seeing the same session used simultaneously by a download and a discovery/metadata call, which was causing cookie invalidation even with private cookie copies. Downloads still run immediately without waiting for metadata. Background discovery is the one that yields. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -55,10 +55,25 @@ _meta_lock = threading.Lock()
|
||||
_meta_last_call: float = 0.0
|
||||
_META_MIN_GAP = 5.0 # seconds between any two metadata requests
|
||||
|
||||
# Active download counter — _meta_run pauses while any download is running so
|
||||
# background discovery and a concurrent download never share the YouTube session
|
||||
# at the same time. Downloads set/clear this; _meta_run reads it.
|
||||
_active_downloads: int = 0
|
||||
_active_downloads_lock = threading.Lock()
|
||||
|
||||
|
||||
def _meta_run(args: list[str], timeout: int = 60) -> tuple[str, str, int]:
|
||||
global _meta_last_call
|
||||
with _meta_lock:
|
||||
# Pause background metadata calls while a download is active.
|
||||
# Running both concurrently causes YouTube to see two requests from the
|
||||
# same session simultaneously, which triggers cookie invalidation.
|
||||
while True:
|
||||
with _active_downloads_lock:
|
||||
if _active_downloads == 0:
|
||||
break
|
||||
time.sleep(3)
|
||||
|
||||
now = time.monotonic()
|
||||
wait = _META_MIN_GAP - (now - _meta_last_call)
|
||||
if wait > 0:
|
||||
@@ -884,6 +899,14 @@ def start_download(
|
||||
)
|
||||
|
||||
def _run_download():
|
||||
global _active_downloads
|
||||
# Signal to _meta_run that a download is active so it pauses all
|
||||
# background discovery/metadata calls for the duration. Running both
|
||||
# concurrently causes YouTube to see the same session used simultaneously
|
||||
# and invalidates cookies.
|
||||
with _active_downloads_lock:
|
||||
_active_downloads += 1
|
||||
try:
|
||||
with _SEMAPHORE:
|
||||
cookie_args = _cookie_args()
|
||||
print(f"[ytdlp] cookie_args={cookie_args!r}", flush=True)
|
||||
@@ -897,9 +920,6 @@ def start_download(
|
||||
*subtitle_args,
|
||||
*cookie_args,
|
||||
]
|
||||
# Private cookie copy — download runs for minutes; without this,
|
||||
# concurrent metadata calls would write-back to the same cookie file
|
||||
# and corrupt the session.
|
||||
cmd, tmp_cookie_path = _make_private_cookie_copy(cmd)
|
||||
try:
|
||||
process = subprocess.Popen(
|
||||
@@ -941,6 +961,9 @@ def start_download(
|
||||
os.unlink(tmp_cookie_path)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
with _active_downloads_lock:
|
||||
_active_downloads -= 1
|
||||
|
||||
thread = threading.Thread(target=_run_download, daemon=True)
|
||||
thread.start()
|
||||
|
||||
Reference in New Issue
Block a user