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
|
# (Required without Docker) Path to your local whisper.cpp CLI binary for STT timestamp generation
|
||||||
WHISPER_CPP_BIN=/whisper.cpp/build/bin/whisper-cli
|
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=
|
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
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
FFMPEG_BIN: /usr/bin/ffmpeg
|
FFMPEG_BIN: /usr/bin/ffmpeg
|
||||||
FFPROBE_BIN: /usr/bin/ffprobe
|
|
||||||
USE_EMBEDDED_WEED_MINI: true
|
USE_EMBEDDED_WEED_MINI: true
|
||||||
BASE_URL: http://127.0.0.1:3003
|
BASE_URL: http://127.0.0.1:3003
|
||||||
S3_ENDPOINT: http://127.0.0.1:8333
|
S3_ENDPOINT: http://127.0.0.1:8333
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ FROM node:lts-alpine AS runner
|
||||||
|
|
||||||
# Add runtime OS dependencies:
|
# Add runtime OS dependencies:
|
||||||
# - libreoffice-writer: required for DOCX → PDF conversion
|
# - 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
|
RUN apk add --no-cache ca-certificates libreoffice-writer
|
||||||
|
|
||||||
# Install pnpm globally for running the app
|
# 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
|
## What works on Vercel
|
||||||
|
|
||||||
- Documents (PDF/EPUB/TXT/MD) work with `POSTGRES_URL` + external S3 storage.
|
- 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
|
:::warning DOCX Conversion Limitation
|
||||||
`docx` conversion requires `soffice` (LibreOffice), which is not available in a standard Vercel runtime.
|
`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).
|
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`
|
||||||
- `/api/audiobook/chapter`
|
- `/api/audiobook/chapter`
|
||||||
|
|
@ -75,7 +75,7 @@ For all variables and defaults, see [Environment Variables](../reference/environ
|
||||||
- `/api/whisper`
|
- `/api/whisper`
|
||||||
|
|
||||||
:::info
|
:::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.
|
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) |
|
| `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 |
|
| `WHISPER_CPP_BIN` | Word timing | unset | Set to enable `whisper.cpp` timestamps |
|
||||||
| `FFMPEG_BIN` | Audio runtime | auto-detected (`ffmpeg-static`) | Override ffmpeg binary path |
|
| `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
|
## 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`
|
- Resolution order: `FFMPEG_BIN` -> `ffmpeg-static`
|
||||||
- Example: `/var/task/node_modules/ffmpeg-static/ffmpeg`
|
- 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",
|
"epubjs": "^0.3.93",
|
||||||
"fast-xml-parser": "^5.3.5",
|
"fast-xml-parser": "^5.3.5",
|
||||||
"ffmpeg-static": "^5.3.0",
|
"ffmpeg-static": "^5.3.0",
|
||||||
"ffprobe-static": "^3.1.0",
|
|
||||||
"howler": "^2.2.4",
|
"howler": "^2.2.4",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"lru-cache": "^11.2.6",
|
"lru-cache": "^11.2.6",
|
||||||
|
|
@ -65,7 +64,6 @@
|
||||||
"@playwright/test": "^1.58.2",
|
"@playwright/test": "^1.58.2",
|
||||||
"@tailwindcss/typography": "^0.5.19",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@types/better-sqlite3": "^7.6.13",
|
"@types/better-sqlite3": "^7.6.13",
|
||||||
"@types/ffprobe-static": "^2.0.3",
|
|
||||||
"@types/howler": "^2.2.12",
|
"@types/howler": "^2.2.12",
|
||||||
"@types/node": "^20.19.33",
|
"@types/node": "^20.19.33",
|
||||||
"@types/pg": "^8.16.0",
|
"@types/pg": "^8.16.0",
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,6 @@ importers:
|
||||||
ffmpeg-static:
|
ffmpeg-static:
|
||||||
specifier: ^5.3.0
|
specifier: ^5.3.0
|
||||||
version: 5.3.0
|
version: 5.3.0
|
||||||
ffprobe-static:
|
|
||||||
specifier: ^3.1.0
|
|
||||||
version: 3.1.0
|
|
||||||
howler:
|
howler:
|
||||||
specifier: ^2.2.4
|
specifier: ^2.2.4
|
||||||
version: 2.2.4
|
version: 2.2.4
|
||||||
|
|
@ -137,9 +134,6 @@ importers:
|
||||||
'@types/better-sqlite3':
|
'@types/better-sqlite3':
|
||||||
specifier: ^7.6.13
|
specifier: ^7.6.13
|
||||||
version: 7.6.13
|
version: 7.6.13
|
||||||
'@types/ffprobe-static':
|
|
||||||
specifier: ^2.0.3
|
|
||||||
version: 2.0.3
|
|
||||||
'@types/howler':
|
'@types/howler':
|
||||||
specifier: ^2.2.12
|
specifier: ^2.2.12
|
||||||
version: 2.2.12
|
version: 2.2.12
|
||||||
|
|
@ -1395,9 +1389,6 @@ packages:
|
||||||
'@types/estree@1.0.8':
|
'@types/estree@1.0.8':
|
||||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
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':
|
'@types/hast@3.0.4':
|
||||||
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
||||||
|
|
||||||
|
|
@ -2406,9 +2397,6 @@ packages:
|
||||||
resolution: {integrity: sha512-H+K6sW6TiIX6VGend0KQwthe+kaceeH/luE8dIZyOP35ik7ahYojDuqlTV1bOrtEwl01sy2HFNGQfi5IDJvotg==}
|
resolution: {integrity: sha512-H+K6sW6TiIX6VGend0KQwthe+kaceeH/luE8dIZyOP35ik7ahYojDuqlTV1bOrtEwl01sy2HFNGQfi5IDJvotg==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
ffprobe-static@3.1.0:
|
|
||||||
resolution: {integrity: sha512-Dvpa9uhVMOYivhHKWLGDoa512J751qN1WZAIO+Xw4L/mrUSPxS4DApzSUDbCFE/LUq2+xYnznEahTd63AqBSpA==}
|
|
||||||
|
|
||||||
file-entry-cache@8.0.0:
|
file-entry-cache@8.0.0:
|
||||||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
@ -5306,8 +5294,6 @@ snapshots:
|
||||||
|
|
||||||
'@types/estree@1.0.8': {}
|
'@types/estree@1.0.8': {}
|
||||||
|
|
||||||
'@types/ffprobe-static@2.0.3': {}
|
|
||||||
|
|
||||||
'@types/hast@3.0.4':
|
'@types/hast@3.0.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 3.0.3
|
'@types/unist': 3.0.3
|
||||||
|
|
@ -6397,8 +6383,6 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
ffprobe-static@3.1.0: {}
|
|
||||||
|
|
||||||
file-entry-cache@8.0.0:
|
file-entry-cache@8.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
flat-cache: 4.0.1
|
flat-cache: 4.0.1
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import {
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
const { Pool } = require('pg');
|
const { Pool } = require('pg');
|
||||||
const BetterSqlite3 = require('better-sqlite3');
|
const BetterSqlite3 = require('better-sqlite3');
|
||||||
const ffprobeStatic = require('ffprobe-static');
|
const ffmpegStatic = require('ffmpeg-static');
|
||||||
|
|
||||||
const DOCSTORE_DIR = path.join(process.cwd(), 'docstore');
|
const DOCSTORE_DIR = path.join(process.cwd(), 'docstore');
|
||||||
const DOCUMENTS_V1_DIR = path.join(DOCSTORE_DIR, 'documents_v1');
|
const DOCUMENTS_V1_DIR = path.join(DOCSTORE_DIR, 'documents_v1');
|
||||||
|
|
@ -321,34 +321,29 @@ function chooseBinary(preferred, bundled, envVarName, packageName) {
|
||||||
return bundledValue;
|
return bundledValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFFprobePath() {
|
function getFFmpegPath() {
|
||||||
return chooseBinary(process.env.FFPROBE_BIN || null, ffprobeStatic?.path || null, 'FFPROBE_BIN', 'ffprobe-static');
|
return chooseBinary(process.env.FFMPEG_BIN || null, ffmpegStatic || null, 'FFMPEG_BIN', 'ffmpeg-static');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ffprobeTitleTag(filePath) {
|
async function ffprobeTitleTag(filePath) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const child = spawn(getFFprobePath(), [
|
const child = spawn(getFFmpegPath(), [
|
||||||
'-v', 'quiet',
|
'-i', filePath,
|
||||||
'-print_format', 'json',
|
'-f', 'ffmetadata',
|
||||||
'-show_entries', 'format_tags=title',
|
'-',
|
||||||
filePath,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let output = '';
|
let stdout = '';
|
||||||
child.stdout.on('data', (data) => { output += data.toString(); });
|
child.stdout.on('data', (data) => { stdout += data.toString(); });
|
||||||
child.on('error', () => resolve(null));
|
child.on('error', () => resolve(null));
|
||||||
child.on('close', (code) => {
|
child.on('close', () => {
|
||||||
if (code !== 0) {
|
const line = stdout.split(/\r?\n/).find((l) => l.startsWith('title='));
|
||||||
|
if (!line) {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
const raw = line.slice('title='.length).trim();
|
||||||
const parsed = JSON.parse(output);
|
resolve(raw.length > 0 ? raw : null);
|
||||||
const title = parsed?.format?.tags?.title;
|
|
||||||
resolve(typeof title === 'string' ? title : null);
|
|
||||||
} catch {
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue