From a916ff3f92ce96aea4be168cf6d1a9886eaef13f Mon Sep 17 00:00:00 2001 From: NimBold Date: Thu, 11 Jun 2026 04:24:40 +0330 Subject: [PATCH] fix: prevent youtube metadata extraction timeout This fixes an issue where node child processes spawned by yt-dlp would keep standard output pipes open, preventing the EOF event from firing and causing the application to hang until a 30s timeout occurred. --- CHANGELOG.md | 2 ++ Sources/Firelink/MediaExtractionEngine.swift | 11 ++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 467a867..472c30c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Applied rate-limiting and text length bounds to the custom `firelink://` scheme to mitigate DoS and injection attempts. ### Fixes +- Fixed a metadata extraction timeout when downloading from YouTube by preventing child processes from holding process pipes open. +- Resolved an issue to correctly assign filenames for auto-captured downloads. - Restored the UUID fallback for token generation to prevent silent failures if secure random byte generation fails. - Hardened local API security by immediately rejecting requests if the expected pairing token is completely empty. - Implemented a thread-safe cleanup mechanism for temporary directories to resolve a concurrency race condition during engine cancellation. diff --git a/Sources/Firelink/MediaExtractionEngine.swift b/Sources/Firelink/MediaExtractionEngine.swift index 1c07d92..a4f35ac 100644 --- a/Sources/Firelink/MediaExtractionEngine.swift +++ b/Sources/Firelink/MediaExtractionEngine.swift @@ -452,25 +452,19 @@ private final class YTDLPMetadataProcess: @unchecked Sendable { process.standardError = errorPipe process.standardInput = nil - let readGroup = DispatchGroup() - - readGroup.enter() outputPipe.fileHandleForReading.readabilityHandler = { handle in let data = handle.availableData if data.isEmpty { handle.readabilityHandler = nil - readGroup.leave() } else { outputBuffer.append(data) } } - readGroup.enter() errorPipe.fileHandleForReading.readabilityHandler = { handle in let data = handle.availableData if data.isEmpty { handle.readabilityHandler = nil - readGroup.leave() } else { errorBuffer.append(data) } @@ -481,7 +475,10 @@ private final class YTDLPMetadataProcess: @unchecked Sendable { } process.terminationHandler = { finishedProcess in - readGroup.notify(queue: .global()) { + // Allow a brief moment for final pipe data to flush + DispatchQueue.global().asyncAfter(deadline: .now() + 0.25) { + outputPipe.fileHandleForReading.readabilityHandler = nil + errorPipe.fileHandleForReading.readabilityHandler = nil if finishedProcess.terminationStatus == 0 { continuation.resume(returning: outputBuffer.data) } else {