@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.
Files changed (153) hide show
  1. package/changelogs/2026-04-18_v0.2.1.md +5 -0
  2. package/changelogs/2026-04-18_v0.3.0.md +6 -0
  3. package/dist/AssetsOptimizeOptions.d.ts +17 -0
  4. package/dist/AssetsOptimizeOptions.d.ts.map +1 -0
  5. package/dist/AssetsOptimizeOptions.js +1 -0
  6. package/dist/AssetsOptimizeResult.d.ts +12 -0
  7. package/dist/AssetsOptimizeResult.d.ts.map +1 -0
  8. package/dist/AssetsOptimizeResult.js +1 -0
  9. package/dist/assetsOptimize.d.ts +4 -0
  10. package/dist/assetsOptimize.d.ts.map +1 -0
  11. package/dist/assetsOptimize.js +60 -0
  12. package/dist/image/ExpectedImage.d.ts +5 -0
  13. package/dist/image/ExpectedImage.d.ts.map +1 -0
  14. package/dist/image/ExpectedImage.js +1 -0
  15. package/dist/image/ImageFormat.d.ts +2 -0
  16. package/dist/image/ImageFormat.d.ts.map +1 -0
  17. package/dist/image/ImageFormat.js +1 -0
  18. package/dist/image/OptimizeImagesOptions.d.ts +10 -0
  19. package/dist/image/OptimizeImagesOptions.d.ts.map +1 -0
  20. package/dist/image/OptimizeImagesOptions.js +1 -0
  21. package/dist/image/ProcessImagesOptions.d.ts +10 -0
  22. package/dist/image/ProcessImagesOptions.d.ts.map +1 -0
  23. package/dist/image/ProcessImagesOptions.js +1 -0
  24. package/dist/image/TransformSpec.d.ts +8 -0
  25. package/dist/image/TransformSpec.d.ts.map +1 -0
  26. package/dist/image/TransformSpec.js +1 -0
  27. package/dist/image/buildExpectedImages.d.ts +4 -0
  28. package/dist/image/buildExpectedImages.d.ts.map +1 -0
  29. package/dist/image/buildExpectedImages.js +89 -0
  30. package/dist/image/createOutputHash.d.ts +2 -0
  31. package/dist/image/createOutputHash.d.ts.map +1 -0
  32. package/dist/image/createOutputHash.js +4 -0
  33. package/dist/image/createOutputHashForFile.d.ts +2 -0
  34. package/dist/image/createOutputHashForFile.d.ts.map +1 -0
  35. package/dist/image/createOutputHashForFile.js +14 -0
  36. package/dist/image/createRootImageTransform.d.ts +3 -0
  37. package/dist/image/createRootImageTransform.d.ts.map +1 -0
  38. package/dist/image/createRootImageTransform.js +33 -0
  39. package/dist/image/defaultQuality.d.ts +2 -0
  40. package/dist/image/defaultQuality.d.ts.map +1 -0
  41. package/dist/image/defaultQuality.js +1 -0
  42. package/dist/image/imageFormats.d.ts +3 -0
  43. package/dist/image/imageFormats.d.ts.map +1 -0
  44. package/dist/image/imageFormats.js +1 -0
  45. package/dist/image/isImageFormat.d.ts +3 -0
  46. package/dist/image/isImageFormat.d.ts.map +1 -0
  47. package/dist/image/isImageFormat.js +5 -0
  48. package/dist/image/optimizeImages.d.ts +4 -0
  49. package/dist/image/optimizeImages.d.ts.map +1 -0
  50. package/dist/image/optimizeImages.js +35 -0
  51. package/dist/image/parseTransformSpec.d.ts +3 -0
  52. package/dist/image/parseTransformSpec.d.ts.map +1 -0
  53. package/dist/image/parseTransformSpec.js +26 -0
  54. package/dist/image/processImage.d.ts +3 -0
  55. package/dist/image/processImage.d.ts.map +1 -0
  56. package/dist/image/processImage.js +28 -0
  57. package/dist/image/processImages.d.ts +3 -0
  58. package/dist/image/processImages.d.ts.map +1 -0
  59. package/dist/image/processImages.js +19 -0
  60. package/dist/image/supportedSourceExtensions.d.ts +2 -0
  61. package/dist/image/supportedSourceExtensions.d.ts.map +1 -0
  62. package/dist/image/supportedSourceExtensions.js +1 -0
  63. package/dist/index.d.ts +31 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +30 -0
  66. package/dist/list/AssetListTypes.d.ts +13 -0
  67. package/dist/list/AssetListTypes.d.ts.map +1 -0
  68. package/dist/list/AssetListTypes.js +1 -0
  69. package/dist/list/formatGeneratedCodeFile.d.ts +3 -0
  70. package/dist/list/formatGeneratedCodeFile.d.ts.map +1 -0
  71. package/dist/list/formatGeneratedCodeFile.js +32 -0
  72. package/dist/list/generateImageList.d.ts +3 -0
  73. package/dist/list/generateImageList.d.ts.map +1 -0
  74. package/dist/list/generateImageList.js +85 -0
  75. package/dist/list/generateVideoList.d.ts +3 -0
  76. package/dist/list/generateVideoList.d.ts.map +1 -0
  77. package/dist/list/generateVideoList.js +87 -0
  78. package/dist/list/getAssetKey.d.ts +2 -0
  79. package/dist/list/getAssetKey.d.ts.map +1 -0
  80. package/dist/list/getAssetKey.js +10 -0
  81. package/dist/list/loadExistingAssetList.d.ts +2 -0
  82. package/dist/list/loadExistingAssetList.d.ts.map +1 -0
  83. package/dist/list/loadExistingAssetList.js +23 -0
  84. package/dist/list/sortAssetMap.d.ts +2 -0
  85. package/dist/list/sortAssetMap.d.ts.map +1 -0
  86. package/dist/list/sortAssetMap.js +8 -0
  87. package/dist/rclone/bisync.d.ts +9 -0
  88. package/dist/rclone/bisync.d.ts.map +1 -0
  89. package/dist/rclone/bisync.js +25 -0
  90. package/dist/rclone/runRclone.d.ts +2 -0
  91. package/dist/rclone/runRclone.d.ts.map +1 -0
  92. package/dist/rclone/runRclone.js +13 -0
  93. package/dist/shared/assertNever.d.ts +2 -0
  94. package/dist/shared/assertNever.d.ts.map +1 -0
  95. package/dist/shared/assertNever.js +3 -0
  96. package/dist/shared/dirExists.d.ts +2 -0
  97. package/dist/shared/dirExists.d.ts.map +1 -0
  98. package/dist/shared/dirExists.js +10 -0
  99. package/dist/shared/getOwnPackageName.d.ts +2 -0
  100. package/dist/shared/getOwnPackageName.d.ts.map +1 -0
  101. package/dist/shared/getOwnPackageName.js +21 -0
  102. package/dist/shared/isMissingDirError.d.ts +2 -0
  103. package/dist/shared/isMissingDirError.d.ts.map +1 -0
  104. package/dist/shared/isMissingDirError.js +3 -0
  105. package/dist/shared/listLocalFiles.d.ts +2 -0
  106. package/dist/shared/listLocalFiles.d.ts.map +1 -0
  107. package/dist/shared/listLocalFiles.js +13 -0
  108. package/dist/shared/logger.d.ts +13 -0
  109. package/dist/shared/logger.d.ts.map +1 -0
  110. package/dist/shared/logger.js +47 -0
  111. package/dist/shared/printSummary.d.ts +4 -0
  112. package/dist/shared/printSummary.d.ts.map +1 -0
  113. package/dist/shared/printSummary.js +25 -0
  114. package/dist/shared/walkFiles.d.ts +2 -0
  115. package/dist/shared/walkFiles.d.ts.map +1 -0
  116. package/dist/shared/walkFiles.js +17 -0
  117. package/dist/video/OptimizeVideosOptions.d.ts +10 -0
  118. package/dist/video/OptimizeVideosOptions.d.ts.map +1 -0
  119. package/dist/video/OptimizeVideosOptions.js +1 -0
  120. package/dist/video/ProcessVideosOptions.d.ts +11 -0
  121. package/dist/video/ProcessVideosOptions.d.ts.map +1 -0
  122. package/dist/video/ProcessVideosOptions.js +1 -0
  123. package/dist/video/createVideoArgs.d.ts +3 -0
  124. package/dist/video/createVideoArgs.d.ts.map +1 -0
  125. package/dist/video/createVideoArgs.js +77 -0
  126. package/dist/video/createVideoPreviewArgs.d.ts +2 -0
  127. package/dist/video/createVideoPreviewArgs.d.ts.map +1 -0
  128. package/dist/video/createVideoPreviewArgs.js +18 -0
  129. package/dist/video/createVideoPreviewPath.d.ts +2 -0
  130. package/dist/video/createVideoPreviewPath.d.ts.map +1 -0
  131. package/dist/video/createVideoPreviewPath.js +3 -0
  132. package/dist/video/ensureVideoPreviews.d.ts +4 -0
  133. package/dist/video/ensureVideoPreviews.d.ts.map +1 -0
  134. package/dist/video/ensureVideoPreviews.js +30 -0
  135. package/dist/video/getAvailableVideoEncoders.d.ts +3 -0
  136. package/dist/video/getAvailableVideoEncoders.d.ts.map +1 -0
  137. package/dist/video/getAvailableVideoEncoders.js +36 -0
  138. package/dist/video/optimizeVideos.d.ts +4 -0
  139. package/dist/video/optimizeVideos.d.ts.map +1 -0
  140. package/dist/video/optimizeVideos.js +81 -0
  141. package/dist/video/processLocalVideos.d.ts +4 -0
  142. package/dist/video/processLocalVideos.d.ts.map +1 -0
  143. package/dist/video/processLocalVideos.js +26 -0
  144. package/dist/video/processVideos.d.ts +3 -0
  145. package/dist/video/processVideos.d.ts.map +1 -0
  146. package/dist/video/processVideos.js +10 -0
  147. package/dist/video/runFfmpeg.d.ts +3 -0
  148. package/dist/video/runFfmpeg.d.ts.map +1 -0
  149. package/dist/video/runFfmpeg.js +26 -0
  150. package/dist/video/supportedVideoSourceExtensions.d.ts +2 -0
  151. package/dist/video/supportedVideoSourceExtensions.d.ts.map +1 -0
  152. package/dist/video/supportedVideoSourceExtensions.js +1 -0
  153. 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,3 @@
1
+ import type { Logger } from "../shared/logger.js";
2
+ export declare function createVideoArgs(sourcePath: string, outputPath: string, extension: string, cwd: string, logger?: Logger): Promise<string[]>;
3
+ //# sourceMappingURL=createVideoArgs.d.ts.map
@@ -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,2 @@
1
+ export declare function createVideoPreviewArgs(inputPath: string, outputPath: string, quality: number): string[];
2
+ //# sourceMappingURL=createVideoPreviewArgs.d.ts.map
@@ -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,2 @@
1
+ export declare function createVideoPreviewPath(videoPath: string): string;
2
+ //# sourceMappingURL=createVideoPreviewPath.d.ts.map
@@ -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,3 @@
1
+ export function createVideoPreviewPath(videoPath) {
2
+ return videoPath.replace(/\.[^.]+$/, ".jpg");
3
+ }
@@ -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,3 @@
1
+ import type { Logger } from "../shared/logger.js";
2
+ export declare function getAvailableVideoEncoders(cwd: string, logger?: Logger): Promise<Set<string>>;
3
+ //# sourceMappingURL=getAvailableVideoEncoders.d.ts.map
@@ -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,3 @@
1
+ import type { ProcessVideosOptions } from "./ProcessVideosOptions.js";
2
+ export declare function processVideos(options: ProcessVideosOptions): Promise<void>;
3
+ //# sourceMappingURL=processVideos.d.ts.map
@@ -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,3 @@
1
+ import type { Logger } from "../shared/logger.js";
2
+ export declare function runFfmpeg(args: string[], cwd: string, logger?: Logger): Promise<void>;
3
+ //# sourceMappingURL=runFfmpeg.d.ts.map
@@ -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,2 @@
1
+ export declare const supportedVideoSourceExtensions: Set<string>;
2
+ //# sourceMappingURL=supportedVideoSourceExtensions.d.ts.map
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adaptive-ds/assets-optimizer",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "main": "./dist/index.js",
5
5
  "dependencies": {
6
6
  "image-size": "^2.0.2",