diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 28aed43..b814879 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -2,7 +2,9 @@ Firelink distributes separate executable tools. Firelink's MIT license does not replace their licenses. -Exact versions, target hashes, sources, and build descriptions are pinned in `engines.lock.json`. +Exact versions, target hashes, sources, and build descriptions are pinned in +`engines.lock.json` for the packaged macOS payload and `engine-sources.lock.json` +for the provisioned Windows and Linux payloads. ## Bundled fonts @@ -27,9 +29,14 @@ License text: - Source: - License: GNU General Public License version 2 or later -Corresponding source for the distributed version is available from the source link and release tag listed in `engines.lock.json`. Firelink release notes must retain that source reference. +Corresponding source for the distributed version is available from the source +link and release tag listed in the applicable engine lock file. Firelink release +notes must retain that source reference. -Linux x64 uses a checksum-pinned musl static build produced from upstream aria2 by . Builder source and upstream tag are recorded in `engine-sources.lock.json`. +Windows and Linux use checksum-pinned upstream aria2 source archives built with +Firelink's reviewed native-DNS, network-target, and allocation-telemetry patch. +The archive, patch checksum, and build provenance are recorded in +`engine-sources.lock.json`. ## FFmpeg @@ -56,4 +63,6 @@ Firelink uses a self-contained PyInstaller onedir distribution. Embedded Python Engine payloads may contain OpenSSL, SQLite, c-ares, libssh2, gettext/libintl, zstd, and other runtime libraries. Their copyright and license notices remain part of their source distributions and embedded package metadata. -Release engineering must review each newly added target payload before adding its hashes to `engines.lock.json`. Missing provenance or license data blocks release. +Release engineering must review each newly added target payload before adding its +hashes to the applicable engine lock file. Missing provenance or license data +blocks release. diff --git a/engine-sources.lock.json b/engine-sources.lock.json index 6dff525..0d9bb99 100644 --- a/engine-sources.lock.json +++ b/engine-sources.lock.json @@ -13,9 +13,9 @@ "sha256": "15e5300b0ba3c3695a7621d90160a746ec9e710228cee639afa9d580f6e3cd11" }, "ffmpeg": { - "version": "9.0.1-11-ge47273f4d9", - "url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-09-04-14-01/ffmpeg-n9.0.1-11-ge47273f4d9-win64-gpl-9.0.zip", - "sha256": "41d735c9364a8deda25b3bd5f05abf37720316be9495edfe94f51bc088ce9d86" + "version": "9.0.1-26-g5c8e7e2433", + "url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-09-06-13-06/ffmpeg-n9.0.1-26-g5c8e7e2433-win64-gpl-9.0.zip", + "sha256": "dd232ccf8661f837a1faa5f534a1a0bdbdb25c42afe79391e8345154df78f791" }, "aria2c": { "version": "1.37.0-firelink-native-dns-v1", @@ -45,9 +45,9 @@ "sha256": "394f07f4da2bebe6ce6f1e7ce0fa16429b29b08c35e3fac3fe25972676dff4b2" }, "ffmpeg": { - "version": "9.0.1-11-ge47273f4d9", - "url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-09-04-14-01/ffmpeg-n9.0.1-11-ge47273f4d9-linux64-gpl-9.0.tar.xz", - "sha256": "1a4fa0f89f690bd81bddbb7cf3a65554095e8e9f9dc98693494e6f5fb97918c5" + "version": "9.0.1-26-g5c8e7e2433", + "url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-09-06-13-06/ffmpeg-n9.0.1-26-g5c8e7e2433-linux64-gpl-9.0.tar.xz", + "sha256": "e60c4187c792cc35d2558adbae5582c470713f5afed7212999200340f1394f8d" }, "aria2c": { "version": "1.37.0-firelink-native-dns-v1", diff --git a/package-lock.json b/package-lock.json index 3a13fe2..80fa463 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@tauri-apps/plugin-notification": "^2.4.0", "@tauri-apps/plugin-opener": "^2.5.5", "i18next": "^26.4.2", - "lucide-react": "^1.41.0", + "lucide-react": "^1.42.0", "react": "^19.2.8", "react-dom": "^19.2.8", "react-i18next": "^17.0.13", @@ -2043,9 +2043,9 @@ } }, "node_modules/lucide-react": { - "version": "1.41.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.41.0.tgz", - "integrity": "sha512-6lksP35l6KszDKUeRTi4LV7i6DEe0Yzl2ALJm9j4c5xEYN91GdW1xGsawGMOg2mgjF5GHBVX8pKX9kP+cWsP3Q==", + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.42.0.tgz", + "integrity": "sha512-b3jprplnoLS8n5etw1z8xODe3hF/yjKATrTitsrKrnjUhCef5BdDct6Ppv3zVvzFwmtfWgLO6XNM3C9fAD93ug==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" diff --git a/package.json b/package.json index 1c800c7..d86b7ac 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@tauri-apps/plugin-notification": "^2.4.0", "@tauri-apps/plugin-opener": "^2.5.5", "i18next": "^26.4.2", - "lucide-react": "^1.41.0", + "lucide-react": "^1.42.0", "react": "^19.2.8", "react-dom": "^19.2.8", "react-i18next": "^17.0.13", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 76cb7a8..0aea1c7 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -749,6 +749,12 @@ dependencies = [ "libc", ] +[[package]] +name = "core_detect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" + [[package]] name = "cpubits" version = "0.1.1" @@ -784,18 +790,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.16" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" +checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" [[package]] name = "crunchy" @@ -1240,11 +1246,17 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" [[package]] name = "encoding_rs" -version = "0.8.35" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "2a7a45518d2863d18aa47f4a0cf9faec2aa4304cc09df5e41299f276b3ad135e" dependencies = [ "cfg-if", + "core_detect", + "multiversion", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", ] [[package]] @@ -1955,9 +1967,9 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32069d97bb81e38fa67eab65e3393bf804bb85969f2bc06bf13f64aef5aba248" +checksum = "a596f1b20ed2cc5ecac41a164aaebc7258057060f06c0cf7a2ba3991ee7990fb" dependencies = [ "hashbrown 0.17.1", ] @@ -2329,9 +2341,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.12.1" +version = "2.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" [[package]] name = "is-docker" @@ -2795,6 +2807,34 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "multiversion" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edb7f0ff51249dfda9ab96b5823695e15a052dc15074c9dbf3d118afaf2c201" +dependencies = [ + "multiversion-macros", + "target-features", +] + +[[package]] +name = "multiversion-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093064383341eb3271f42e381cb8f10a01459478446953953c75d24bd339fc0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "target-features", +] + +[[package]] +name = "multiversion_no_op" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" + [[package]] name = "ndk" version = "0.9.0" @@ -3407,13 +3447,13 @@ checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "plist" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85" +checksum = "2896bade328c13f7042a297ea5ac5b0951f6cf989dea5f32c2fd98da398195cb" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "indexmap 2.14.2", - "quick-xml", + "quick-xml 0.42.0", "serde", "time", ] @@ -3586,6 +3626,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.47" @@ -3870,9 +3919,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.43" +version = "0.23.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" +checksum = "6725596c3f2c3a0aef021139e145d4eafe314a6623e4680ca83852b2c67ab2ba" dependencies = [ "once_cell", "ring", @@ -4666,6 +4715,12 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "target-features" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1bbb9f3c5c463a01705937a24fdabc5047929ac764b2d5b9cf681c1f5041ed5" + [[package]] name = "target-lexicon" version = "0.12.16" @@ -5952,7 +6007,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.41.0", "quote", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 420d82d..2e92bdb 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -32,7 +32,7 @@ serde_json = "1" tokio = { version = "1", features = ["fs", "process", "io-util", "rt", "rt-multi-thread", "macros", "sync", "time"] } regex = "1.10" reqwest = { version = "0.13", default-features = false, features = ["rustls-no-provider", "json", "stream", "socks"] } -rustls = { version = "0.23.43", default-features = false, features = ["ring", "std", "tls12"] } +rustls = { version = "0.23.44", default-features = false, features = ["ring", "std", "tls12"] } uuid = { version = "1", features = ["v4"] } ts-rs = { version = "12", features = ["serde-compat", "uuid-impl"] } tauri-plugin-notification = "2.4.0" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index cc11ffb..ed5af23 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1035,6 +1035,19 @@ fn drain_media_output_lines(buffer: &mut String, chunk: &str) -> Vec { lines } +fn append_bounded_stderr_tail(tail: &mut String, chunk: &str, max_bytes: usize) { + tail.push_str(chunk); + if tail.len() <= max_bytes { + return; + } + + let start = tail.len() - max_bytes; + let boundary = (start..tail.len()) + .find(|&index| tail.is_char_boundary(index)) + .unwrap_or(tail.len()); + tail.drain(..boundary); +} + fn flush_media_output_line(buffer: &mut String) -> Option { let line = std::mem::take(buffer); (!line.trim().is_empty()).then_some(line) @@ -5052,10 +5065,7 @@ pub(crate) async fn start_media_download_internal( } Some(tauri_plugin_shell::process::CommandEvent::Stderr(line_bytes)) => { let chunk = String::from_utf8_lossy(&line_bytes); - stderr_tail.push_str(&chunk); - if stderr_tail.len() > STDERR_TAIL { - stderr_tail = stderr_tail.split_off(stderr_tail.len() - STDERR_TAIL); - } + append_bounded_stderr_tail(&mut stderr_tail, &chunk, STDERR_TAIL); for line in drain_media_output_lines(&mut stderr_buffer, &chunk) { if let Some(progress) = parse_media_progress_line(&line) { emit_media_progress( @@ -14249,7 +14259,7 @@ mod tests { use super::{ aggregate_media_byte_progress, aggregate_media_fraction, append_ytdlp_config_option, - append_ytdlp_http_headers, + append_bounded_stderr_tail, append_ytdlp_http_headers, build_media_format_options, collect_download_uris, drain_media_output_lines, filename_from_content_disposition, filename_from_url_disposition_query, filename_from_url_path, is_excluded_yt_dlp_format, @@ -18035,6 +18045,24 @@ mod tests { assert!(buffer.is_empty()); } + #[test] + fn bounds_stderr_tail_without_splitting_utf8_across_appends() { + const MAX_BYTES: usize = 7; + let mut tail = String::new(); + + for chunk in ["a", "🙂", "é", "🙂", "b", "🙂", "c", "🙂"] { + append_bounded_stderr_tail(&mut tail, chunk, MAX_BYTES); + assert!(tail.len() <= MAX_BYTES); + } + + for _ in 0..32 { + append_bounded_stderr_tail(&mut tail, "🙂", MAX_BYTES); + assert!(tail.len() <= MAX_BYTES); + } + + assert_eq!(tail, "🙂"); + } + #[test] fn parses_structured_ytdlp_numeric_percent_without_total() { let line = format!(