diff --git a/engine-sources.lock.json b/engine-sources.lock.json index 9821dc8..a4be654 100644 --- a/engine-sources.lock.json +++ b/engine-sources.lock.json @@ -13,9 +13,9 @@ "sha256": "15e5300b0ba3c3695a7621d90160a746ec9e710228cee639afa9d580f6e3cd11" }, "ffmpeg": { - "version": "8.1.2-50-g1a748fe2cd", - "url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-09-02-13-13/ffmpeg-n8.1.2-50-g1a748fe2cd-win64-gpl-8.1.zip", - "sha256": "954c2f5a219d6d34280393ceaf611777f6fcc96b785263328fb263df35dc1f8a" + "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" }, "aria2c": { "version": "1.37.0", @@ -35,9 +35,9 @@ "sha256": "394f07f4da2bebe6ce6f1e7ce0fa16429b29b08c35e3fac3fe25972676dff4b2" }, "ffmpeg": { - "version": "8.1.2-50-g1a748fe2cd", - "url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-09-02-13-13/ffmpeg-n8.1.2-50-g1a748fe2cd-linux64-gpl-8.1.tar.xz", - "sha256": "f3b7cc13863b6e934c5245c12383bec545490f49a3ccf6bf90ac42c55dd76f10" + "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" }, "aria2c": { "version": "1.37.0", diff --git a/engines.lock.json b/engines.lock.json index 664d6b3..b96edfe 100644 --- a/engines.lock.json +++ b/engines.lock.json @@ -22,11 +22,12 @@ "sha256": "b6e51125007860e1a58c75737faa2cd0d1d5372c1c9ca82d1bf87d2c40081aac" }, "ffmpeg": { - "version": "N-126314-g3386acd2f9", + "version": "9.0.1", "source": "https://ffmpeg.org/", - "build": "GPLv3 build identified by binary as https://www.martin-riedl.de", - "url": "https://ffmpeg.martin-riedl.de/download/macos/arm64/1788094033_N-126314-g3386acd2f9/ffmpeg.zip", - "sha256": "d817e43743539cd78755eac32478b5deff95e35a105e44e75758395d6b26e181" + "build": "Stable GPLv3 build identified by binary as https://www.martin-riedl.de", + "url": "https://ffmpeg.martin-riedl.de/download/macos/arm64/1787073674_9.0.1/ffmpeg.zip", + "sourceSha256": "8287a1b2229e05eb41859f073e18e6c52c60a778f2f5e6881070fe51b79407fe", + "sha256": "393e4c395020a1cb7cbd77fbe00599ce69d1c6466fee0dbd59d13f86a81a1611" }, "deno": { "version": "2.9.6", diff --git a/scripts/check-updates.js b/scripts/check-updates.js index c86aaa4..cc8f889 100644 --- a/scripts/check-updates.js +++ b/scripts/check-updates.js @@ -324,7 +324,14 @@ function sourceEngineVersions(sourceLock) { const rows = []; for (const [target, engines] of Object.entries(sourceLock.targets || {})) { for (const [engine, meta] of Object.entries(engines)) { - rows.push({ target, engine, version: meta.version, url: meta.url, sha256: meta.sha256 }); + rows.push({ + target, + engine, + version: meta.version, + url: meta.url, + sha256: meta.sha256, + sourceSha256: meta.sourceSha256, + }); } } return rows; @@ -334,7 +341,14 @@ function packagedEngineVersions(engineLock) { const rows = []; for (const [target, targetLock] of Object.entries(engineLock.targets || {})) { for (const [engine, meta] of Object.entries(targetLock.engines || {})) { - rows.push({ target, engine, version: meta.version, url: meta.url, sha256: meta.sha256 }); + rows.push({ + target, + engine, + version: meta.version, + url: meta.url, + sha256: meta.sha256, + sourceSha256: meta.sourceSha256, + }); } } return rows; @@ -373,7 +387,8 @@ function checkRows( const versionOutdated = compareVersions(current, wanted) < 0; const sourceOutdated = Boolean(latestUrl && row.url && row.url !== latestUrl); const latestHash = latestHashesByTargetEngine[targetKey] || latestHashesByUrl[row.url]; - const currentHash = typeof row.sha256 === 'string' ? row.sha256.toLowerCase() : ''; + const checkedHash = row.sourceSha256 || row.sha256; + const currentHash = typeof checkedHash === 'string' ? checkedHash.toLowerCase() : ''; const hashOutdated = Boolean(latestHash && currentHash !== latestHash); const status = versionOutdated ? 'outdated' @@ -385,7 +400,7 @@ function checkRows( if (status !== 'current') outdated += 1; console.log(` ${row.target} ${row.engine}: ${current} -> ${wanted} ${status}`); if (sourceOutdated) console.log(` source: ${row.url} -> ${latestUrl}`); - if (hashOutdated) console.log(` sha256: ${row.sha256 || 'missing'} -> ${latestHash}`); + if (hashOutdated) console.log(` source sha256: ${checkedHash || 'missing'} -> ${latestHash}`); } return outdated; } diff --git a/scripts/check-updates.node-test.js b/scripts/check-updates.node-test.js index e8d18ea..ea2ecfc 100644 --- a/scripts/check-updates.node-test.js +++ b/scripts/check-updates.node-test.js @@ -95,6 +95,26 @@ test('checkRows detects a provider hash change when version and URL are current' assert.equal(outdated, 1); }); +test('checkRows compares packaged source provenance without confusing the payload digest', () => { + const outdated = checkRows( + [{ + target: 'aarch64-apple-darwin', + engine: 'ffmpeg', + version: '9.0.1', + url: 'https://example.test/ffmpeg.zip', + sourceSha256: 'a'.repeat(64), + sha256: 'b'.repeat(64), + }], + { ffmpeg: '9.0.1' }, + { 'aarch64-apple-darwin:ffmpeg': '9.0.1' }, + { 'aarch64-apple-darwin:ffmpeg': 'https://example.test/ffmpeg.zip' }, + new Set(['ffmpeg']), + { 'aarch64-apple-darwin:ffmpeg': 'a'.repeat(64) }, + ); + + assert.equal(outdated, 0); +}); + test('checkRows detects an aria2 asset digest change when the provider supplies it', () => { const url = 'https://github.com/aria2/aria2/releases/download/release-1.37.0/aria2-1.37.0-win-64bit-build1.zip'; const digest = 'b'.repeat(64); diff --git a/scripts/engine-download.node-test.js b/scripts/engine-download.node-test.js index 6bc77f4..0edbb1d 100644 --- a/scripts/engine-download.node-test.js +++ b/scripts/engine-download.node-test.js @@ -225,3 +225,32 @@ test('propagates external cancellation without retrying an in-flight archive', a fs.rmSync(directory, { recursive: true, force: true }); } }); + +test('rejects and removes an archive with a mismatched checksum', async () => { + const { directory, archive } = makeArchivePath(); + const corrupt = Buffer.from('corrupt engine archive'); + + try { + await withMockFetch( + async () => new Response(corrupt, { + status: 200, + headers: { 'Content-Length': String(corrupt.length) }, + }), + async () => { + await assert.rejects( + downloadEngineArchive({ + name: 'ffmpeg', + url: 'https://example.test/ffmpeg.zip', + archive, + expectedSha256: digest(Buffer.from('trusted engine archive')), + attempts: 1, + }), + /Archive checksum mismatch for ffmpeg/, + ); + }, + ); + assert.equal(fs.existsSync(archive), false); + } finally { + fs.rmSync(directory, { recursive: true, force: true }); + } +}); diff --git a/src-tauri/binaries/ffmpeg-aarch64-apple-darwin b/src-tauri/binaries/ffmpeg-aarch64-apple-darwin index dad02c5..30be566 100755 Binary files a/src-tauri/binaries/ffmpeg-aarch64-apple-darwin and b/src-tauri/binaries/ffmpeg-aarch64-apple-darwin differ diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9782f44..6f3ad6e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -17925,6 +17925,13 @@ mod tests { assert_eq!(parse_ffmpeg_version(output), Some("8.1.2".to_string())); } + #[test] + fn parses_martin_riedl_ffmpeg_nine_stable_version() { + let output = "ffmpeg version 9.0.1-https://www.martin-riedl.de Copyright (c) 2000-2026 the FFmpeg developers"; + + assert_eq!(parse_ffmpeg_version(output), Some("9.0.1".to_string())); + } + #[test] fn uses_fragment_progress_instead_of_temporary_hls_size_estimates() { let line = format!(