@adaptive-ds/assets-optimizer 0.1.0 → 0.2.1
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/README.md +36 -15
- package/bun.lock +12 -12
- package/changelogs/2026-04-18_v0.2.0.md +32 -0
- package/changelogs/2026-04-18_v0.2.1.md +5 -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 +61 -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/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/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 +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -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 +17 -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/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/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
- package/assets-optimizer.code-workspace +0 -8
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
export async function getOwnPackageName(moduleUrl) {
|
|
5
|
+
let currentDir = path.dirname(fileURLToPath(moduleUrl));
|
|
6
|
+
while (true) {
|
|
7
|
+
const packageJsonPath = path.join(currentDir, "package.json");
|
|
8
|
+
try {
|
|
9
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
10
|
+
if (packageJson.name) {
|
|
11
|
+
return packageJson.name;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
catch { }
|
|
15
|
+
const parentDir = path.dirname(currentDir);
|
|
16
|
+
if (parentDir === currentDir) {
|
|
17
|
+
throw new Error(`Could not resolve package name for ${moduleUrl}`);
|
|
18
|
+
}
|
|
19
|
+
currentDir = parentDir;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isMissingDirError.d.ts","sourceRoot":"","sources":["../../src/shared/isMissingDirError.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,cAAc,CAEhF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listLocalFiles.d.ts","sourceRoot":"","sources":["../../src/shared/listLocalFiles.ts"],"names":[],"mappings":"AAGA,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAUnE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { isMissingDirError } from "./isMissingDirError.js";
|
|
2
|
+
import { walkFiles } from "./walkFiles.js";
|
|
3
|
+
export async function listLocalFiles(dir) {
|
|
4
|
+
try {
|
|
5
|
+
return await walkFiles(dir);
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
if (isMissingDirError(error)) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
throw error;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type LogLevel = 0 | 1 | 2 | 3;
|
|
2
|
+
export interface Logger {
|
|
3
|
+
readonly level: LogLevel;
|
|
4
|
+
isEnabled(level: LogLevel): boolean;
|
|
5
|
+
summary(message: string): void;
|
|
6
|
+
files(message: string): void;
|
|
7
|
+
cli(message: string): void;
|
|
8
|
+
verbose(message: string): void;
|
|
9
|
+
warn(message: string): void;
|
|
10
|
+
error(message: string): void;
|
|
11
|
+
}
|
|
12
|
+
export declare function createLogger(logLevel: number | undefined): Logger;
|
|
13
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/shared/logger.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAEpC,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;IACxB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAA;IACnC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAiCjE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function createLogger(logLevel) {
|
|
2
|
+
const level = normalizeLogLevel(logLevel);
|
|
3
|
+
const log = (minimumLevel, message, write = console.log) => {
|
|
4
|
+
if (level >= minimumLevel) {
|
|
5
|
+
write(message);
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
return {
|
|
9
|
+
level,
|
|
10
|
+
isEnabled(minimumLevel) {
|
|
11
|
+
return level >= minimumLevel;
|
|
12
|
+
},
|
|
13
|
+
summary(message) {
|
|
14
|
+
log(1, message);
|
|
15
|
+
},
|
|
16
|
+
files(message) {
|
|
17
|
+
log(1, message);
|
|
18
|
+
},
|
|
19
|
+
cli(message) {
|
|
20
|
+
log(2, message);
|
|
21
|
+
},
|
|
22
|
+
verbose(message) {
|
|
23
|
+
log(3, message);
|
|
24
|
+
},
|
|
25
|
+
warn(message) {
|
|
26
|
+
log(1, message, console.warn);
|
|
27
|
+
},
|
|
28
|
+
error(message) {
|
|
29
|
+
log(1, message, console.error);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function normalizeLogLevel(logLevel) {
|
|
34
|
+
if (logLevel == null) {
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
if (logLevel <= 0) {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
if (logLevel === 1) {
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
if (logLevel === 2) {
|
|
44
|
+
return 2;
|
|
45
|
+
}
|
|
46
|
+
return 3;
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"printSummary.d.ts","sourceRoot":"","sources":["../../src/shared/printSummary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,wBAAgB,YAAY,CAAC,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA6B/E"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function printSummary(result, logger) {
|
|
2
|
+
logger.summary(`Processed ${result.processed.length} new optimized images`);
|
|
3
|
+
logger.summary(`Skipped ${result.skippedExisting.length} existing optimized images`);
|
|
4
|
+
logger.summary(`Skipped ${result.skippedRootFiles.length} root original files`);
|
|
5
|
+
logger.summary(`Deleted ${result.deletedLocal.length} stale local optimized images`);
|
|
6
|
+
logger.summary(`Processed ${result.processedVideos.length} new optimized videos`);
|
|
7
|
+
logger.summary(`Skipped ${result.skippedExistingVideos.length} existing processed videos`);
|
|
8
|
+
logger.summary(`Generated ${result.processedVideoPreviews.length} new video previews`);
|
|
9
|
+
logger.summary(`Skipped ${result.skippedExistingVideoPreviews.length} existing video previews`);
|
|
10
|
+
for (const fileName of result.processed) {
|
|
11
|
+
logger.files(`processed image: ${fileName}`);
|
|
12
|
+
}
|
|
13
|
+
for (const fileName of result.deletedLocal) {
|
|
14
|
+
logger.files(`deleted local image: ${fileName}`);
|
|
15
|
+
}
|
|
16
|
+
for (const fileName of result.processedVideos) {
|
|
17
|
+
logger.files(`processed video: ${fileName}`);
|
|
18
|
+
}
|
|
19
|
+
for (const fileName of result.processedVideoPreviews) {
|
|
20
|
+
logger.files(`generated preview: ${fileName}`);
|
|
21
|
+
}
|
|
22
|
+
for (const warning of result.warnings) {
|
|
23
|
+
logger.warn(warning);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walkFiles.d.ts","sourceRoot":"","sources":["../../src/shared/walkFiles.ts"],"names":[],"mappings":"AAGA,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAiB9D"}
|
|
@@ -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,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 { 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"]);
|
package/package.json
CHANGED