diff --git a/packages/ffmpeg/src/ffmpeg-version.ts b/packages/ffmpeg/src/ffmpeg-version.ts index 41d9b2d89..f737bc254 100644 --- a/packages/ffmpeg/src/ffmpeg-version.ts +++ b/packages/ffmpeg/src/ffmpeg-version.ts @@ -1,6 +1,9 @@ import { exec } from 'child_process' import ffmpeg from 'fluent-ffmpeg' +/** + * @returns FFmpeg version string. Usually a semver string, but may vary when depending on installation method. + */ export function getFFmpegVersion () { return new Promise((res, rej) => { (ffmpeg() as any)._getFfmpegPath((err, ffmpegPath) => { @@ -10,14 +13,10 @@ export function getFFmpegVersion () { return exec(`${ffmpegPath} -version`, (err, stdout) => { if (err) return rej(err) - const parsed = stdout.match(/ffmpeg version .?(\d+\.\d+(\.\d+)?)/) - if (!parsed?.[1]) return rej(new Error(`Could not find ffmpeg version in ${stdout}`)) + const parsed = stdout.match(/(?<=ffmpeg version )[a-zA-Z\d.-]+/) + if (!parsed) return rej(new Error(`Could not find ffmpeg version in ${stdout}`)) - // Fix ffmpeg version that does not include patch version (4.4 for example) - let version = parsed[1] - if (version.match(/^\d+\.\d+$/)) { - version += '.0' - } + res(parsed[0]) }) }) }) diff --git a/packages/tests/src/server-helpers/core-utils.ts b/packages/tests/src/server-helpers/core-utils.ts index 06c78591e..d61cae855 100644 --- a/packages/tests/src/server-helpers/core-utils.ts +++ b/packages/tests/src/server-helpers/core-utils.ts @@ -5,7 +5,7 @@ import snakeCase from 'lodash-es/snakeCase.js' import validator from 'validator' import { getAverageTheoreticalBitrate, getMaxTheoreticalBitrate } from '@peertube/peertube-core-utils' import { VideoResolution } from '@peertube/peertube-models' -import { objectConverter, parseBytes, parseDurationToMs } from '@peertube/peertube-server/server/helpers/core-utils.js' +import { objectConverter, parseBytes, parseDurationToMs, parseSemVersion } from '@peertube/peertube-server/server/helpers/core-utils.js' describe('Parse Bytes', function () { @@ -148,3 +148,54 @@ describe('Bitrate', function () { } }) }) + +describe('Parse semantic version string', function () { + + it('Should parse Node.js version string', function () { + const actual = parseSemVersion('v18.16.0') + + expect(actual.major).to.equal(18) + expect(actual.minor).to.equal(16) + expect(actual.patch).to.equal(0) + }) + + it('Should parse FFmpeg version string from Debian 12 repo', function () { + const actual = parseSemVersion('5.1.3-1') + + expect(actual.major).to.equal(5) + expect(actual.minor).to.equal(1) + expect(actual.patch).to.equal(3) + }) + + it('Should parse FFmpeg version string from Arch repo', function () { + const actual = parseSemVersion('n6.0') + + expect(actual.major).to.equal(6) + expect(actual.minor).to.equal(0) + expect(actual.patch).to.equal(0) + }) + + it('Should parse FFmpeg version from GitHub release', function () { + const actual = parseSemVersion('5.1.3') + + expect(actual.major).to.equal(5) + expect(actual.minor).to.equal(1) + expect(actual.patch).to.equal(3) + }) + + it('Should parse FFmpeg version from GitHub dev release', function () { + const actual = parseSemVersion('5.1.git') + + expect(actual.major).to.equal(5) + expect(actual.minor).to.equal(1) + expect(actual.patch).to.equal(0) + }) + + it('Should parse FFmpeg version string with missing patch segment', function () { + const actual = parseSemVersion('4.4') + + expect(actual.major).to.equal(4) + expect(actual.minor).to.equal(4) + expect(actual.patch).to.equal(0) + }) +}) diff --git a/server/server/helpers/core-utils.ts b/server/server/helpers/core-utils.ts index 6dc09d317..9b40ca5be 100644 --- a/server/server/helpers/core-utils.ts +++ b/server/server/helpers/core-utils.ts @@ -183,13 +183,23 @@ function pageToStartAndCount (page: number, itemsPerPage: number) { // --------------------------------------------------------------------------- type SemVersion = { major: number, minor: number, patch: number } + +/** + * Parses a semantic version string into its separate components. + * Fairly lax, and allows for missing or additional segments in the string. + * + * @param s String to parse semantic version from. + * @returns Major, minor, and patch version, or null if string does not follow semantic version conventions. + */ function parseSemVersion (s: string) { - const parsed = s.match(/^v?(\d+)\.(\d+)\.(\d+)$/i) + const parsed = s.match(/v?(\d+)\.(\d+)(?:\.(\d+))?/i) + + if (!parsed) return null return { major: parseInt(parsed[1]), minor: parseInt(parsed[2]), - patch: parseInt(parsed[3]) + patch: parsed[3] ? parseInt(parsed[3]) : 0 } as SemVersion } diff --git a/server/server/initializers/checker-after-init.ts b/server/server/initializers/checker-after-init.ts index afcf6176b..5e7e513f1 100644 --- a/server/server/initializers/checker-after-init.ts +++ b/server/server/initializers/checker-after-init.ts @@ -78,7 +78,14 @@ async function applicationExist () { async function checkFFmpegVersion () { const version = await getFFmpegVersion() - const { major, minor, patch } = parseSemVersion(version) + const semvar = parseSemVersion(version) + + if (!semvar) { + logger.warn('Your ffmpeg version (%s) does not use semvar. Unable to determine version compatibility.', version) + return + } + + const { major, minor, patch } = semvar if (major < 4 || (major === 4 && minor < 1)) { logger.warn('Your ffmpeg version (%s) is outdated. PeerTube supports ffmpeg >= 4.1. Please upgrade ffmpeg.', version)