refactor(deps): remove ffprobe-static and fix playwright flakiness
Replace ffprobe-static with ffmpeg-static for metadata extraction in migration scripts. This removes the requirement for ffprobe binaries across Docker, CI, and Vercel environments, simplifying the dependency graph.
This commit is contained in:
parent
da0fce708f
commit
fa406a8cb2
8 changed files with 20 additions and 53 deletions
|
|
@ -72,6 +72,5 @@ IMPORT_LIBRARY_DIRS=
|
|||
# (Required without Docker) Path to your local whisper.cpp CLI binary for STT timestamp generation
|
||||
WHISPER_CPP_BIN=/whisper.cpp/build/bin/whisper-cli
|
||||
|
||||
# (Optional) Override ffmpeg/ffprobe binary paths used for audiobook processing/probing
|
||||
# (Optional) Override ffmpeg binary path used for audiobook processing
|
||||
FFMPEG_BIN=
|
||||
FFPROBE_BIN=
|
||||
|
|
|
|||
1
.github/workflows/playwright.yml
vendored
1
.github/workflows/playwright.yml
vendored
|
|
@ -10,7 +10,6 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
env:
|
||||
FFMPEG_BIN: /usr/bin/ffmpeg
|
||||
FFPROBE_BIN: /usr/bin/ffprobe
|
||||
USE_EMBEDDED_WEED_MINI: true
|
||||
BASE_URL: http://127.0.0.1:3003
|
||||
S3_ENDPOINT: http://127.0.0.1:8333
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ FROM node:lts-alpine AS runner
|
|||
|
||||
# Add runtime OS dependencies:
|
||||
# - libreoffice-writer: required for DOCX → PDF conversion
|
||||
# ffmpeg/ffprobe are provided by ffmpeg-static/ffprobe-static from node_modules.
|
||||
# ffmpeg is provided by ffmpeg-static from node_modules.
|
||||
RUN apk add --no-cache ca-certificates libreoffice-writer
|
||||
|
||||
# Install pnpm globally for running the app
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ This guide covers deploying OpenReader WebUI to Vercel with external Postgres an
|
|||
## What works on Vercel
|
||||
|
||||
- Documents (PDF/EPUB/TXT/MD) work with `POSTGRES_URL` + external S3 storage.
|
||||
- Audiobook routes work on Node.js serverless functions using `ffmpeg-static`/`ffprobe-static`.
|
||||
- Audiobook routes work on Node.js serverless functions using `ffmpeg-static`.
|
||||
|
||||
:::warning DOCX Conversion Limitation
|
||||
`docx` conversion requires `soffice` (LibreOffice), which is not available in a standard Vercel runtime.
|
||||
|
|
@ -65,9 +65,9 @@ For internet-exposed Vercel deployments, set both `BASE_URL` and `AUTH_SECRET`.
|
|||
For all variables and defaults, see [Environment Variables](../reference/environment-variables).
|
||||
:::
|
||||
|
||||
## 2. FFmpeg/ffprobe packaging in Vercel functions
|
||||
## 2. FFmpeg packaging in Vercel functions
|
||||
|
||||
`ffmpeg-static` and `ffprobe-static` binaries must be included in function traces. This repo already does that in `next.config.ts` via `outputFileTracingIncludes` for:
|
||||
`ffmpeg-static` binaries must be included in function traces. This repo already does that in `next.config.ts` via `outputFileTracingIncludes` for:
|
||||
|
||||
- `/api/audiobook`
|
||||
- `/api/audiobook/chapter`
|
||||
|
|
@ -75,7 +75,7 @@ For all variables and defaults, see [Environment Variables](../reference/environ
|
|||
- `/api/whisper`
|
||||
|
||||
:::info
|
||||
`serverExternalPackages` should include `ffmpeg-static` and `ffprobe-static` so package paths resolve at runtime instead of being bundled into route output.
|
||||
`serverExternalPackages` should include `ffmpeg-static` so package paths resolve at runtime instead of being bundled into route output.
|
||||
:::
|
||||
|
||||
If you change route paths or split handlers, update `outputFileTracingIncludes` accordingly.
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ This is the single reference page for OpenReader WebUI environment variables.
|
|||
| `IMPORT_LIBRARY_DIRS` | Library import | unset | Set multiple roots (comma/colon/semicolon separated) |
|
||||
| `WHISPER_CPP_BIN` | Word timing | unset | Set to enable `whisper.cpp` timestamps |
|
||||
| `FFMPEG_BIN` | Audio runtime | auto-detected (`ffmpeg-static`) | Override ffmpeg binary path |
|
||||
| `FFPROBE_BIN` | Audio runtime | auto-detected (`ffprobe-static`) | Override ffprobe binary path |
|
||||
|
||||
## Client Runtime and Feature Flags
|
||||
|
||||
|
|
@ -369,10 +368,3 @@ Absolute path or executable name for the ffmpeg binary used by audiobook/process
|
|||
|
||||
- Resolution order: `FFMPEG_BIN` -> `ffmpeg-static`
|
||||
- Example: `/var/task/node_modules/ffmpeg-static/ffmpeg`
|
||||
|
||||
### FFPROBE_BIN
|
||||
|
||||
Absolute path or executable name for the ffprobe binary used for audio probing.
|
||||
|
||||
- Resolution order: `FFPROBE_BIN` -> `ffprobe-static`
|
||||
- Example: `/var/task/node_modules/ffprobe-static/bin/linux/x64/ffprobe`
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
"epubjs": "^0.3.93",
|
||||
"fast-xml-parser": "^5.3.5",
|
||||
"ffmpeg-static": "^5.3.0",
|
||||
"ffprobe-static": "^3.1.0",
|
||||
"howler": "^2.2.4",
|
||||
"jszip": "^3.10.1",
|
||||
"lru-cache": "^11.2.6",
|
||||
|
|
@ -65,7 +64,6 @@
|
|||
"@playwright/test": "^1.58.2",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/ffprobe-static": "^2.0.3",
|
||||
"@types/howler": "^2.2.12",
|
||||
"@types/node": "^20.19.33",
|
||||
"@types/pg": "^8.16.0",
|
||||
|
|
|
|||
|
|
@ -67,9 +67,6 @@ importers:
|
|||
ffmpeg-static:
|
||||
specifier: ^5.3.0
|
||||
version: 5.3.0
|
||||
ffprobe-static:
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0
|
||||
howler:
|
||||
specifier: ^2.2.4
|
||||
version: 2.2.4
|
||||
|
|
@ -137,9 +134,6 @@ importers:
|
|||
'@types/better-sqlite3':
|
||||
specifier: ^7.6.13
|
||||
version: 7.6.13
|
||||
'@types/ffprobe-static':
|
||||
specifier: ^2.0.3
|
||||
version: 2.0.3
|
||||
'@types/howler':
|
||||
specifier: ^2.2.12
|
||||
version: 2.2.12
|
||||
|
|
@ -1395,9 +1389,6 @@ packages:
|
|||
'@types/estree@1.0.8':
|
||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||
|
||||
'@types/ffprobe-static@2.0.3':
|
||||
resolution: {integrity: sha512-86Q8dIzhjknxA6935ZcVLkVU3t6bP+F08mdriWZk2+3UqjxIbT3QEADjbO5Yq8826cVs/aZBREVZ7jUoSTgHiw==}
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
||||
|
||||
|
|
@ -2406,9 +2397,6 @@ packages:
|
|||
resolution: {integrity: sha512-H+K6sW6TiIX6VGend0KQwthe+kaceeH/luE8dIZyOP35ik7ahYojDuqlTV1bOrtEwl01sy2HFNGQfi5IDJvotg==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
ffprobe-static@3.1.0:
|
||||
resolution: {integrity: sha512-Dvpa9uhVMOYivhHKWLGDoa512J751qN1WZAIO+Xw4L/mrUSPxS4DApzSUDbCFE/LUq2+xYnznEahTd63AqBSpA==}
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
|
@ -5306,8 +5294,6 @@ snapshots:
|
|||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@types/ffprobe-static@2.0.3': {}
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
|
@ -6397,8 +6383,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ffprobe-static@3.1.0: {}
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
dependencies:
|
||||
flat-cache: 4.0.1
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
const require = createRequire(import.meta.url);
|
||||
const { Pool } = require('pg');
|
||||
const BetterSqlite3 = require('better-sqlite3');
|
||||
const ffprobeStatic = require('ffprobe-static');
|
||||
const ffmpegStatic = require('ffmpeg-static');
|
||||
|
||||
const DOCSTORE_DIR = path.join(process.cwd(), 'docstore');
|
||||
const DOCUMENTS_V1_DIR = path.join(DOCSTORE_DIR, 'documents_v1');
|
||||
|
|
@ -321,34 +321,29 @@ function chooseBinary(preferred, bundled, envVarName, packageName) {
|
|||
return bundledValue;
|
||||
}
|
||||
|
||||
function getFFprobePath() {
|
||||
return chooseBinary(process.env.FFPROBE_BIN || null, ffprobeStatic?.path || null, 'FFPROBE_BIN', 'ffprobe-static');
|
||||
function getFFmpegPath() {
|
||||
return chooseBinary(process.env.FFMPEG_BIN || null, ffmpegStatic || null, 'FFMPEG_BIN', 'ffmpeg-static');
|
||||
}
|
||||
|
||||
async function ffprobeTitleTag(filePath) {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn(getFFprobePath(), [
|
||||
'-v', 'quiet',
|
||||
'-print_format', 'json',
|
||||
'-show_entries', 'format_tags=title',
|
||||
filePath,
|
||||
const child = spawn(getFFmpegPath(), [
|
||||
'-i', filePath,
|
||||
'-f', 'ffmetadata',
|
||||
'-',
|
||||
]);
|
||||
|
||||
let output = '';
|
||||
child.stdout.on('data', (data) => { output += data.toString(); });
|
||||
let stdout = '';
|
||||
child.stdout.on('data', (data) => { stdout += data.toString(); });
|
||||
child.on('error', () => resolve(null));
|
||||
child.on('close', (code) => {
|
||||
if (code !== 0) {
|
||||
child.on('close', () => {
|
||||
const line = stdout.split(/\r?\n/).find((l) => l.startsWith('title='));
|
||||
if (!line) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(output);
|
||||
const title = parsed?.format?.tags?.title;
|
||||
resolve(typeof title === 'string' ? title : null);
|
||||
} catch {
|
||||
resolve(null);
|
||||
}
|
||||
const raw = line.slice('title='.length).trim();
|
||||
resolve(raw.length > 0 ? raw : null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue