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