@adaptive-ds/assets-optimizer 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelogs/2026-04-18_v0.2.1.md +5 -0
- package/changelogs/2026-04-18_v0.3.0.md +6 -0
- package/dist/AssetsOptimizeOptions.d.ts +17 -0
- package/dist/AssetsOptimizeOptions.d.ts.map +1 -0
- package/dist/AssetsOptimizeOptions.js +1 -0
- package/dist/AssetsOptimizeResult.d.ts +12 -0
- package/dist/AssetsOptimizeResult.d.ts.map +1 -0
- package/dist/AssetsOptimizeResult.js +1 -0
- package/dist/assetsOptimize.d.ts +4 -0
- package/dist/assetsOptimize.d.ts.map +1 -0
- package/dist/assetsOptimize.js +60 -0
- package/dist/image/ExpectedImage.d.ts +5 -0
- package/dist/image/ExpectedImage.d.ts.map +1 -0
- package/dist/image/ExpectedImage.js +1 -0
- package/dist/image/ImageFormat.d.ts +2 -0
- package/dist/image/ImageFormat.d.ts.map +1 -0
- package/dist/image/ImageFormat.js +1 -0
- package/dist/image/OptimizeImagesOptions.d.ts +10 -0
- package/dist/image/OptimizeImagesOptions.d.ts.map +1 -0
- package/dist/image/OptimizeImagesOptions.js +1 -0
- package/dist/image/ProcessImagesOptions.d.ts +10 -0
- package/dist/image/ProcessImagesOptions.d.ts.map +1 -0
- package/dist/image/ProcessImagesOptions.js +1 -0
- package/dist/image/TransformSpec.d.ts +8 -0
- package/dist/image/TransformSpec.d.ts.map +1 -0
- package/dist/image/TransformSpec.js +1 -0
- package/dist/image/buildExpectedImages.d.ts +4 -0
- package/dist/image/buildExpectedImages.d.ts.map +1 -0
- package/dist/image/buildExpectedImages.js +89 -0
- package/dist/image/createOutputHash.d.ts +2 -0
- package/dist/image/createOutputHash.d.ts.map +1 -0
- package/dist/image/createOutputHash.js +4 -0
- package/dist/image/createOutputHashForFile.d.ts +2 -0
- package/dist/image/createOutputHashForFile.d.ts.map +1 -0
- package/dist/image/createOutputHashForFile.js +14 -0
- package/dist/image/createRootImageTransform.d.ts +3 -0
- package/dist/image/createRootImageTransform.d.ts.map +1 -0
- package/dist/image/createRootImageTransform.js +33 -0
- package/dist/image/defaultQuality.d.ts +2 -0
- package/dist/image/defaultQuality.d.ts.map +1 -0
- package/dist/image/defaultQuality.js +1 -0
- package/dist/image/imageFormats.d.ts +3 -0
- package/dist/image/imageFormats.d.ts.map +1 -0
- package/dist/image/imageFormats.js +1 -0
- package/dist/image/isImageFormat.d.ts +3 -0
- package/dist/image/isImageFormat.d.ts.map +1 -0
- package/dist/image/isImageFormat.js +5 -0
- package/dist/image/optimizeImages.d.ts +4 -0
- package/dist/image/optimizeImages.d.ts.map +1 -0
- package/dist/image/optimizeImages.js +35 -0
- package/dist/image/parseTransformSpec.d.ts +3 -0
- package/dist/image/parseTransformSpec.d.ts.map +1 -0
- package/dist/image/parseTransformSpec.js +26 -0
- package/dist/image/processImage.d.ts +3 -0
- package/dist/image/processImage.d.ts.map +1 -0
- package/dist/image/processImage.js +28 -0
- package/dist/image/processImages.d.ts +3 -0
- package/dist/image/processImages.d.ts.map +1 -0
- package/dist/image/processImages.js +19 -0
- package/dist/image/supportedSourceExtensions.d.ts +2 -0
- package/dist/image/supportedSourceExtensions.d.ts.map +1 -0
- package/dist/image/supportedSourceExtensions.js +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/list/AssetListTypes.d.ts +13 -0
- package/dist/list/AssetListTypes.d.ts.map +1 -0
- package/dist/list/AssetListTypes.js +1 -0
- package/dist/list/formatGeneratedCodeFile.d.ts +3 -0
- package/dist/list/formatGeneratedCodeFile.d.ts.map +1 -0
- package/dist/list/formatGeneratedCodeFile.js +32 -0
- package/dist/list/generateImageList.d.ts +3 -0
- package/dist/list/generateImageList.d.ts.map +1 -0
- package/dist/list/generateImageList.js +85 -0
- package/dist/list/generateVideoList.d.ts +3 -0
- package/dist/list/generateVideoList.d.ts.map +1 -0
- package/dist/list/generateVideoList.js +87 -0
- package/dist/list/getAssetKey.d.ts +2 -0
- package/dist/list/getAssetKey.d.ts.map +1 -0
- package/dist/list/getAssetKey.js +10 -0
- package/dist/list/loadExistingAssetList.d.ts +2 -0
- package/dist/list/loadExistingAssetList.d.ts.map +1 -0
- package/dist/list/loadExistingAssetList.js +23 -0
- package/dist/list/sortAssetMap.d.ts +2 -0
- package/dist/list/sortAssetMap.d.ts.map +1 -0
- package/dist/list/sortAssetMap.js +8 -0
- package/dist/rclone/bisync.d.ts +9 -0
- package/dist/rclone/bisync.d.ts.map +1 -0
- package/dist/rclone/bisync.js +25 -0
- package/dist/rclone/runRclone.d.ts +2 -0
- package/dist/rclone/runRclone.d.ts.map +1 -0
- package/dist/rclone/runRclone.js +13 -0
- package/dist/shared/assertNever.d.ts +2 -0
- package/dist/shared/assertNever.d.ts.map +1 -0
- package/dist/shared/assertNever.js +3 -0
- package/dist/shared/dirExists.d.ts +2 -0
- package/dist/shared/dirExists.d.ts.map +1 -0
- package/dist/shared/dirExists.js +10 -0
- package/dist/shared/getOwnPackageName.d.ts +2 -0
- package/dist/shared/getOwnPackageName.d.ts.map +1 -0
- package/dist/shared/getOwnPackageName.js +21 -0
- package/dist/shared/isMissingDirError.d.ts +2 -0
- package/dist/shared/isMissingDirError.d.ts.map +1 -0
- package/dist/shared/isMissingDirError.js +3 -0
- package/dist/shared/listLocalFiles.d.ts +2 -0
- package/dist/shared/listLocalFiles.d.ts.map +1 -0
- package/dist/shared/listLocalFiles.js +13 -0
- package/dist/shared/logger.d.ts +13 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +47 -0
- package/dist/shared/printSummary.d.ts +4 -0
- package/dist/shared/printSummary.d.ts.map +1 -0
- package/dist/shared/printSummary.js +25 -0
- package/dist/shared/walkFiles.d.ts +2 -0
- package/dist/shared/walkFiles.d.ts.map +1 -0
- package/dist/shared/walkFiles.js +17 -0
- package/dist/video/OptimizeVideosOptions.d.ts +10 -0
- package/dist/video/OptimizeVideosOptions.d.ts.map +1 -0
- package/dist/video/OptimizeVideosOptions.js +1 -0
- package/dist/video/ProcessVideosOptions.d.ts +11 -0
- package/dist/video/ProcessVideosOptions.d.ts.map +1 -0
- package/dist/video/ProcessVideosOptions.js +1 -0
- package/dist/video/createVideoArgs.d.ts +3 -0
- package/dist/video/createVideoArgs.d.ts.map +1 -0
- package/dist/video/createVideoArgs.js +77 -0
- package/dist/video/createVideoPreviewArgs.d.ts +2 -0
- package/dist/video/createVideoPreviewArgs.d.ts.map +1 -0
- package/dist/video/createVideoPreviewArgs.js +18 -0
- package/dist/video/createVideoPreviewPath.d.ts +2 -0
- package/dist/video/createVideoPreviewPath.d.ts.map +1 -0
- package/dist/video/createVideoPreviewPath.js +3 -0
- package/dist/video/ensureVideoPreviews.d.ts +4 -0
- package/dist/video/ensureVideoPreviews.d.ts.map +1 -0
- package/dist/video/ensureVideoPreviews.js +30 -0
- package/dist/video/getAvailableVideoEncoders.d.ts +3 -0
- package/dist/video/getAvailableVideoEncoders.d.ts.map +1 -0
- package/dist/video/getAvailableVideoEncoders.js +36 -0
- package/dist/video/optimizeVideos.d.ts +4 -0
- package/dist/video/optimizeVideos.d.ts.map +1 -0
- package/dist/video/optimizeVideos.js +81 -0
- package/dist/video/processLocalVideos.d.ts +4 -0
- package/dist/video/processLocalVideos.d.ts.map +1 -0
- package/dist/video/processLocalVideos.js +26 -0
- package/dist/video/processVideos.d.ts +3 -0
- package/dist/video/processVideos.d.ts.map +1 -0
- package/dist/video/processVideos.js +10 -0
- package/dist/video/runFfmpeg.d.ts +3 -0
- package/dist/video/runFfmpeg.d.ts.map +1 -0
- package/dist/video/runFfmpeg.js +26 -0
- package/dist/video/supportedVideoSourceExtensions.d.ts +2 -0
- package/dist/video/supportedVideoSourceExtensions.d.ts.map +1 -0
- package/dist/video/supportedVideoSourceExtensions.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function walkFiles(dir) {
|
|
4
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
5
|
+
const files = [];
|
|
6
|
+
for (const entry of entries) {
|
|
7
|
+
const fullPath = path.join(dir, entry.name);
|
|
8
|
+
if (entry.isDirectory()) {
|
|
9
|
+
files.push(...(await walkFiles(fullPath)));
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (entry.isFile()) {
|
|
13
|
+
files.push(fullPath);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return files;
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface OptimizeVideosOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
logLevel?: 0 | 1 | 2 | 3;
|
|
4
|
+
videoOriginalsDir?: string;
|
|
5
|
+
videoOptimizedDir?: string;
|
|
6
|
+
videoListOutputPath?: string;
|
|
7
|
+
generateVideoList?: boolean;
|
|
8
|
+
videoPreviewQuality?: number;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=OptimizeVideosOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OptimizeVideosOptions.d.ts","sourceRoot":"","sources":["../../src/video/OptimizeVideosOptions.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AssetsOptimizeResult } from "../AssetsOptimizeResult.js";
|
|
2
|
+
import type { Logger } from "../shared/logger.js";
|
|
3
|
+
export interface ProcessVideosOptions {
|
|
4
|
+
cwd: string;
|
|
5
|
+
videoOriginalsDir: string;
|
|
6
|
+
videoOptimizedDir: string;
|
|
7
|
+
videoPreviewQuality: number;
|
|
8
|
+
result: AssetsOptimizeResult;
|
|
9
|
+
logger: Logger;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=ProcessVideosOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProcessVideosOptions.d.ts","sourceRoot":"","sources":["../../src/video/ProcessVideosOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAEjD,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,iBAAiB,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,MAAM,CAAA;IACzB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,MAAM,EAAE,oBAAoB,CAAA;IAC5B,MAAM,EAAE,MAAM,CAAA;CACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createVideoArgs.d.ts","sourceRoot":"","sources":["../../src/video/createVideoArgs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAGjD,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,EAAE,CAAC,CA+EnB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { getAvailableVideoEncoders } from "./getAvailableVideoEncoders.js";
|
|
2
|
+
export async function createVideoArgs(sourcePath, outputPath, extension, cwd, logger) {
|
|
3
|
+
const commonArgs = ["-y", "-i", sourcePath, "-map", "0:v:0", "-map", "0:a?", "-map_metadata", "-1"];
|
|
4
|
+
if (extension === ".webm") {
|
|
5
|
+
return [
|
|
6
|
+
...commonArgs,
|
|
7
|
+
"-c:v",
|
|
8
|
+
"libvpx-vp9",
|
|
9
|
+
"-row-mt",
|
|
10
|
+
"1",
|
|
11
|
+
"-crf",
|
|
12
|
+
"36",
|
|
13
|
+
"-b:v",
|
|
14
|
+
"0",
|
|
15
|
+
"-c:a",
|
|
16
|
+
"libopus",
|
|
17
|
+
"-b:a",
|
|
18
|
+
"96k",
|
|
19
|
+
outputPath,
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
const availableEncoders = await getAvailableVideoEncoders(cwd, logger);
|
|
23
|
+
if (availableEncoders.has("libx264")) {
|
|
24
|
+
return [
|
|
25
|
+
...commonArgs,
|
|
26
|
+
"-c:v",
|
|
27
|
+
"libx264",
|
|
28
|
+
"-preset",
|
|
29
|
+
"medium",
|
|
30
|
+
"-crf",
|
|
31
|
+
"28",
|
|
32
|
+
"-pix_fmt",
|
|
33
|
+
"yuv420p",
|
|
34
|
+
"-c:a",
|
|
35
|
+
"aac",
|
|
36
|
+
"-b:a",
|
|
37
|
+
"128k",
|
|
38
|
+
"-movflags",
|
|
39
|
+
"+faststart",
|
|
40
|
+
outputPath,
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
if (availableEncoders.has("libopenh264")) {
|
|
44
|
+
return [
|
|
45
|
+
...commonArgs,
|
|
46
|
+
"-c:v",
|
|
47
|
+
"libopenh264",
|
|
48
|
+
"-b:v",
|
|
49
|
+
"1000k",
|
|
50
|
+
"-pix_fmt",
|
|
51
|
+
"yuv420p",
|
|
52
|
+
"-c:a",
|
|
53
|
+
"aac",
|
|
54
|
+
"-b:a",
|
|
55
|
+
"128k",
|
|
56
|
+
"-movflags",
|
|
57
|
+
"+faststart",
|
|
58
|
+
outputPath,
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
return [
|
|
62
|
+
...commonArgs,
|
|
63
|
+
"-c:v",
|
|
64
|
+
"mpeg4",
|
|
65
|
+
"-q:v",
|
|
66
|
+
"5",
|
|
67
|
+
"-pix_fmt",
|
|
68
|
+
"yuv420p",
|
|
69
|
+
"-c:a",
|
|
70
|
+
"aac",
|
|
71
|
+
"-b:a",
|
|
72
|
+
"128k",
|
|
73
|
+
"-movflags",
|
|
74
|
+
"+faststart",
|
|
75
|
+
outputPath,
|
|
76
|
+
];
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createVideoPreviewArgs.d.ts","sourceRoot":"","sources":["../../src/video/createVideoPreviewArgs.ts"],"names":[],"mappings":"AAAA,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAavG"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function createVideoPreviewArgs(inputPath, outputPath, quality) {
|
|
2
|
+
return [
|
|
3
|
+
"-y",
|
|
4
|
+
"-i",
|
|
5
|
+
inputPath,
|
|
6
|
+
"-vf",
|
|
7
|
+
"thumbnail",
|
|
8
|
+
"-frames:v",
|
|
9
|
+
"1",
|
|
10
|
+
"-q:v",
|
|
11
|
+
String(mapJpegQualityToQScale(quality)),
|
|
12
|
+
outputPath,
|
|
13
|
+
];
|
|
14
|
+
}
|
|
15
|
+
function mapJpegQualityToQScale(quality) {
|
|
16
|
+
const normalizedQuality = Math.max(1, Math.min(100, Math.round(quality)));
|
|
17
|
+
return Math.max(2, Math.min(31, Math.round(31 - ((normalizedQuality - 1) / 99) * 29)));
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createVideoPreviewPath.d.ts","sourceRoot":"","sources":["../../src/video/createVideoPreviewPath.ts"],"names":[],"mappings":"AAAA,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEhE"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AssetsOptimizeResult } from "../AssetsOptimizeResult.js";
|
|
2
|
+
import type { Logger } from "../shared/logger.js";
|
|
3
|
+
export declare function ensureVideoPreviews(videoOptimizedDir: string, videoPreviewQuality: number, cwd: string, result: AssetsOptimizeResult, logger: Logger): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=ensureVideoPreviews.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensureVideoPreviews.d.ts","sourceRoot":"","sources":["../../src/video/ensureVideoPreviews.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAEtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAOjD,wBAAsB,mBAAmB,CACvC,iBAAiB,EAAE,MAAM,EACzB,mBAAmB,EAAE,MAAM,EAC3B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,oBAAoB,EAC5B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { dirExists } from "../shared/dirExists.js";
|
|
4
|
+
import { walkFiles } from "../shared/walkFiles.js";
|
|
5
|
+
import { createVideoPreviewArgs } from "./createVideoPreviewArgs.js";
|
|
6
|
+
import { createVideoPreviewPath } from "./createVideoPreviewPath.js";
|
|
7
|
+
import { runFfmpeg } from "./runFfmpeg.js";
|
|
8
|
+
import { supportedVideoSourceExtensions } from "./supportedVideoSourceExtensions.js";
|
|
9
|
+
export async function ensureVideoPreviews(videoOptimizedDir, videoPreviewQuality, cwd, result, logger) {
|
|
10
|
+
if (!(await dirExists(videoOptimizedDir))) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
for (const filePath of await walkFiles(videoOptimizedDir)) {
|
|
14
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
15
|
+
if (!supportedVideoSourceExtensions.has(extension)) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const previewPath = createVideoPreviewPath(filePath);
|
|
19
|
+
const relativePreviewPath = path.relative(videoOptimizedDir, previewPath);
|
|
20
|
+
try {
|
|
21
|
+
await fs.access(previewPath);
|
|
22
|
+
result.skippedExistingVideoPreviews.push(relativePreviewPath);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
catch { }
|
|
26
|
+
await fs.mkdir(path.dirname(previewPath), { recursive: true });
|
|
27
|
+
await runFfmpeg(createVideoPreviewArgs(filePath, previewPath, videoPreviewQuality), cwd, logger);
|
|
28
|
+
result.processedVideoPreviews.push(relativePreviewPath);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAvailableVideoEncoders.d.ts","sourceRoot":"","sources":["../../src/video/getAvailableVideoEncoders.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAIjD,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAwClG"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as Bun from "bun";
|
|
2
|
+
let availableEncodersPromise;
|
|
3
|
+
export async function getAvailableVideoEncoders(cwd, logger) {
|
|
4
|
+
if (!availableEncodersPromise) {
|
|
5
|
+
availableEncodersPromise = (async () => {
|
|
6
|
+
const command = ["ffmpeg", "-hide_banner", "-encoders"];
|
|
7
|
+
logger?.cli(command.join(" "));
|
|
8
|
+
const process = Bun.spawn(command, {
|
|
9
|
+
cwd,
|
|
10
|
+
stdout: "pipe",
|
|
11
|
+
stderr: "pipe",
|
|
12
|
+
});
|
|
13
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
14
|
+
new Response(process.stdout).text(),
|
|
15
|
+
new Response(process.stderr).text(),
|
|
16
|
+
process.exited,
|
|
17
|
+
]);
|
|
18
|
+
if (exitCode !== 0) {
|
|
19
|
+
throw new Error(`ffmpeg -encoders failed with exit code ${exitCode}\n${stderr || stdout}`.trim());
|
|
20
|
+
}
|
|
21
|
+
if (logger?.isEnabled(3)) {
|
|
22
|
+
if (stdout.trim()) {
|
|
23
|
+
logger.verbose(stdout.trim());
|
|
24
|
+
}
|
|
25
|
+
if (stderr.trim()) {
|
|
26
|
+
logger.verbose(stderr.trim());
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return new Set(stdout
|
|
30
|
+
.split("\n")
|
|
31
|
+
.map((line) => line.trim().split(/\s+/)[1])
|
|
32
|
+
.filter((value) => Boolean(value)));
|
|
33
|
+
})();
|
|
34
|
+
}
|
|
35
|
+
return availableEncodersPromise;
|
|
36
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AssetsOptimizeResult } from "../AssetsOptimizeResult.js";
|
|
2
|
+
import type { OptimizeVideosOptions } from "./OptimizeVideosOptions.js";
|
|
3
|
+
export declare function optimizeVideos(options?: OptimizeVideosOptions): Promise<AssetsOptimizeResult>;
|
|
4
|
+
//# sourceMappingURL=optimizeVideos.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimizeVideos.d.ts","sourceRoot":"","sources":["../../src/video/optimizeVideos.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAEtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAyEvE,wBAAsB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA+BvG"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { dirExists } from "../shared/dirExists.js";
|
|
4
|
+
import { createLogger } from "../shared/logger.js";
|
|
5
|
+
import { printSummary } from "../shared/printSummary.js";
|
|
6
|
+
import { supportedVideoSourceExtensions } from "./supportedVideoSourceExtensions.js";
|
|
7
|
+
import { walkFiles } from "../shared/walkFiles.js";
|
|
8
|
+
import { runFfmpeg } from "./runFfmpeg.js";
|
|
9
|
+
import { createVideoArgs } from "./createVideoArgs.js";
|
|
10
|
+
import { createVideoPreviewArgs } from "./createVideoPreviewArgs.js";
|
|
11
|
+
import { createVideoPreviewPath } from "./createVideoPreviewPath.js";
|
|
12
|
+
import { generateVideoList } from "../list/generateVideoList.js";
|
|
13
|
+
async function processLocalVideos(videoOriginalsDir, videoOptimizedDir, cwd, result, logger) {
|
|
14
|
+
for (const sourceFile of await walkFiles(videoOriginalsDir)) {
|
|
15
|
+
const extension = path.extname(sourceFile).toLowerCase();
|
|
16
|
+
const relativePath = path.relative(videoOriginalsDir, sourceFile);
|
|
17
|
+
if (!supportedVideoSourceExtensions.has(extension)) {
|
|
18
|
+
result.warnings.push(`Skipped unsupported video source file: ${relativePath}`);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const outputPath = path.join(videoOptimizedDir, relativePath);
|
|
22
|
+
try {
|
|
23
|
+
await fs.access(outputPath);
|
|
24
|
+
result.skippedExistingVideos.push(relativePath);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
catch { }
|
|
28
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
29
|
+
await runFfmpeg(await createVideoArgs(sourceFile, outputPath, extension, cwd, logger), cwd, logger);
|
|
30
|
+
result.processedVideos.push(relativePath);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function ensureVideoPreviews(videoOptimizedDir, videoPreviewQuality, cwd, result, logger) {
|
|
34
|
+
if (!(await dirExists(videoOptimizedDir))) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
for (const filePath of await walkFiles(videoOptimizedDir)) {
|
|
38
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
39
|
+
if (!supportedVideoSourceExtensions.has(extension)) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const previewPath = createVideoPreviewPath(filePath);
|
|
43
|
+
const relativePreviewPath = path.relative(videoOptimizedDir, previewPath);
|
|
44
|
+
try {
|
|
45
|
+
await fs.access(previewPath);
|
|
46
|
+
result.skippedExistingVideoPreviews.push(relativePreviewPath);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
catch { }
|
|
50
|
+
await fs.mkdir(path.dirname(previewPath), { recursive: true });
|
|
51
|
+
await runFfmpeg(createVideoPreviewArgs(filePath, previewPath, videoPreviewQuality), cwd, logger);
|
|
52
|
+
result.processedVideoPreviews.push(relativePreviewPath);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function optimizeVideos(options = {}) {
|
|
56
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
57
|
+
const videoOriginalsDir = path.resolve(cwd, options.videoOriginalsDir ?? "videos");
|
|
58
|
+
const videoOptimizedDir = path.resolve(cwd, options.videoOptimizedDir ?? "public/videos");
|
|
59
|
+
const videoListOutputPath = path.resolve(cwd, options.videoListOutputPath ?? "src/app/assets/videoList.ts");
|
|
60
|
+
const logger = createLogger(options.logLevel);
|
|
61
|
+
const result = {
|
|
62
|
+
processed: [],
|
|
63
|
+
skippedExisting: [],
|
|
64
|
+
skippedRootFiles: [],
|
|
65
|
+
warnings: [],
|
|
66
|
+
deletedLocal: [],
|
|
67
|
+
processedVideos: [],
|
|
68
|
+
skippedExistingVideos: [],
|
|
69
|
+
processedVideoPreviews: [],
|
|
70
|
+
skippedExistingVideoPreviews: [],
|
|
71
|
+
};
|
|
72
|
+
if (await dirExists(videoOriginalsDir)) {
|
|
73
|
+
await processLocalVideos(videoOriginalsDir, videoOptimizedDir, cwd, result, logger);
|
|
74
|
+
}
|
|
75
|
+
await ensureVideoPreviews(videoOptimizedDir, options.videoPreviewQuality ?? 80, cwd, result, logger);
|
|
76
|
+
if (options.generateVideoList !== false) {
|
|
77
|
+
await generateVideoList(videoOptimizedDir, videoListOutputPath, undefined, logger);
|
|
78
|
+
}
|
|
79
|
+
printSummary(result, logger);
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AssetsOptimizeResult } from "../AssetsOptimizeResult.js";
|
|
2
|
+
import type { Logger } from "../shared/logger.js";
|
|
3
|
+
export declare function processLocalVideos(videoOriginalsDir: string, videoOptimizedDir: string, cwd: string, result: AssetsOptimizeResult, logger: Logger): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=processLocalVideos.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processLocalVideos.d.ts","sourceRoot":"","sources":["../../src/video/processLocalVideos.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAMjD,wBAAsB,kBAAkB,CACtC,iBAAiB,EAAE,MAAM,EACzB,iBAAiB,EAAE,MAAM,EACzB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,oBAAoB,EAC5B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAsBf"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { walkFiles } from "../shared/walkFiles.js";
|
|
4
|
+
import { createVideoArgs } from "./createVideoArgs.js";
|
|
5
|
+
import { runFfmpeg } from "./runFfmpeg.js";
|
|
6
|
+
import { supportedVideoSourceExtensions } from "./supportedVideoSourceExtensions.js";
|
|
7
|
+
export async function processLocalVideos(videoOriginalsDir, videoOptimizedDir, cwd, result, logger) {
|
|
8
|
+
for (const sourceFile of await walkFiles(videoOriginalsDir)) {
|
|
9
|
+
const extension = path.extname(sourceFile).toLowerCase();
|
|
10
|
+
const relativePath = path.relative(videoOriginalsDir, sourceFile);
|
|
11
|
+
if (!supportedVideoSourceExtensions.has(extension)) {
|
|
12
|
+
result.warnings.push(`Skipped unsupported video source file: ${relativePath}`);
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const outputPath = path.join(videoOptimizedDir, relativePath);
|
|
16
|
+
try {
|
|
17
|
+
await fs.access(outputPath);
|
|
18
|
+
result.skippedExistingVideos.push(relativePath);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
catch { }
|
|
22
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
23
|
+
await runFfmpeg(await createVideoArgs(sourceFile, outputPath, extension, cwd, logger), cwd, logger);
|
|
24
|
+
result.processedVideos.push(relativePath);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processVideos.d.ts","sourceRoot":"","sources":["../../src/video/processVideos.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAGrE,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { dirExists } from "../shared/dirExists.js";
|
|
2
|
+
import { ensureVideoPreviews } from "./ensureVideoPreviews.js";
|
|
3
|
+
import { processLocalVideos } from "./processLocalVideos.js";
|
|
4
|
+
export async function processVideos(options) {
|
|
5
|
+
const { cwd, logger, result, videoOptimizedDir, videoPreviewQuality, videoOriginalsDir } = options;
|
|
6
|
+
if (await dirExists(videoOriginalsDir)) {
|
|
7
|
+
await processLocalVideos(videoOriginalsDir, videoOptimizedDir, cwd, result, logger);
|
|
8
|
+
}
|
|
9
|
+
await ensureVideoPreviews(videoOptimizedDir, videoPreviewQuality, cwd, result, logger);
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runFfmpeg.d.ts","sourceRoot":"","sources":["../../src/video/runFfmpeg.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAEjD,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B3F"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as Bun from "bun";
|
|
2
|
+
export async function runFfmpeg(args, cwd, logger) {
|
|
3
|
+
const command = ["ffmpeg", ...args];
|
|
4
|
+
logger?.cli(command.join(" "));
|
|
5
|
+
const process = Bun.spawn(command, {
|
|
6
|
+
cwd,
|
|
7
|
+
stdout: "pipe",
|
|
8
|
+
stderr: "pipe",
|
|
9
|
+
});
|
|
10
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
11
|
+
new Response(process.stdout).text(),
|
|
12
|
+
new Response(process.stderr).text(),
|
|
13
|
+
process.exited,
|
|
14
|
+
]);
|
|
15
|
+
if (exitCode !== 0) {
|
|
16
|
+
throw new Error(`ffmpeg ${args.join(" ")} failed with exit code ${exitCode}\n${stderr || stdout}`.trim());
|
|
17
|
+
}
|
|
18
|
+
if (logger?.isEnabled(3)) {
|
|
19
|
+
if (stdout.trim()) {
|
|
20
|
+
logger.verbose(stdout.trim());
|
|
21
|
+
}
|
|
22
|
+
if (stderr.trim()) {
|
|
23
|
+
logger.verbose(stderr.trim());
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supportedVideoSourceExtensions.d.ts","sourceRoot":"","sources":["../../src/video/supportedVideoSourceExtensions.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,8BAA8B,aAA6D,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const supportedVideoSourceExtensions = new Set([".mp4", ".mov", ".m4v", ".webm", ".avi", ".mkv"]);
|