@remotion/renderer 4.0.0-oops.3 → 4.0.0-prefetch.10

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 (273) hide show
  1. package/dist/assets/calculate-asset-positions.d.ts +2 -2
  2. package/dist/assets/convert-assets-to-file-urls.d.ts +5 -4
  3. package/dist/assets/convert-assets-to-file-urls.js +2 -2
  4. package/dist/assets/download-and-map-assets-to-file.d.ts +16 -6
  5. package/dist/assets/download-and-map-assets-to-file.js +152 -45
  6. package/dist/assets/download-file.d.ts +10 -5
  7. package/dist/assets/download-file.js +20 -5
  8. package/dist/assets/download-map.d.ts +64 -0
  9. package/dist/assets/download-map.js +73 -0
  10. package/dist/assets/ffmpeg-volume-expression.d.ts +3 -3
  11. package/dist/assets/ffmpeg-volume-expression.js +16 -14
  12. package/dist/assets/flatten-volume-array.d.ts +1 -1
  13. package/dist/assets/get-audio-channels.d.ts +3 -4
  14. package/dist/assets/get-audio-channels.js +14 -4
  15. package/dist/assets/get-video-stream-duration.d.ts +4 -0
  16. package/dist/assets/get-video-stream-duration.js +59 -0
  17. package/dist/assets/read-file.js +4 -1
  18. package/dist/assets/types.d.ts +1 -1
  19. package/dist/browser/Browser.d.ts +60 -0
  20. package/dist/browser/Browser.js +218 -0
  21. package/dist/browser/BrowserConnector.d.ts +19 -0
  22. package/dist/browser/BrowserConnector.js +17 -0
  23. package/dist/browser/BrowserFetcher.d.ts +89 -0
  24. package/dist/browser/BrowserFetcher.js +510 -0
  25. package/dist/browser/BrowserPage.d.ts +74 -0
  26. package/dist/browser/BrowserPage.js +283 -0
  27. package/dist/browser/BrowserRunner.d.ts +39 -0
  28. package/dist/browser/BrowserRunner.js +308 -0
  29. package/dist/browser/Connection.d.ts +42 -0
  30. package/dist/browser/Connection.js +242 -0
  31. package/dist/browser/ConsoleMessage.d.ts +31 -0
  32. package/dist/browser/ConsoleMessage.js +48 -0
  33. package/dist/browser/DOMWorld.d.ts +52 -0
  34. package/dist/browser/DOMWorld.js +272 -0
  35. package/dist/browser/Errors.d.ts +25 -0
  36. package/dist/browser/Errors.js +35 -0
  37. package/dist/browser/EvalTypes.d.ts +27 -0
  38. package/dist/browser/EvalTypes.js +17 -0
  39. package/dist/browser/EventEmitter.d.ts +23 -0
  40. package/dist/browser/EventEmitter.js +53 -0
  41. package/dist/browser/ExecutionContext.d.ts +34 -0
  42. package/dist/browser/ExecutionContext.js +174 -0
  43. package/dist/browser/FrameManager.d.ts +93 -0
  44. package/dist/browser/FrameManager.js +496 -0
  45. package/dist/browser/HTTPRequest.d.ts +28 -0
  46. package/dist/browser/HTTPRequest.js +37 -0
  47. package/dist/browser/HTTPResponse.d.ts +21 -0
  48. package/dist/browser/HTTPResponse.js +41 -0
  49. package/dist/browser/JSHandle.d.ts +35 -0
  50. package/dist/browser/JSHandle.js +90 -0
  51. package/dist/browser/LaunchOptions.d.ts +37 -0
  52. package/dist/browser/LaunchOptions.js +17 -0
  53. package/dist/browser/Launcher.d.ts +9 -0
  54. package/dist/browser/Launcher.js +504 -0
  55. package/dist/browser/LifecycleWatcher.d.ts +29 -0
  56. package/dist/browser/LifecycleWatcher.js +180 -0
  57. package/dist/browser/NetworkEventManager.d.ts +33 -0
  58. package/dist/browser/NetworkEventManager.js +81 -0
  59. package/dist/browser/NetworkManager.d.ts +34 -0
  60. package/dist/browser/NetworkManager.js +231 -0
  61. package/dist/browser/NodeWebSocketTransport.d.ts +17 -0
  62. package/dist/browser/NodeWebSocketTransport.js +87 -0
  63. package/dist/browser/Product.d.ts +16 -0
  64. package/dist/browser/Product.js +17 -0
  65. package/dist/browser/PuppeteerNode.d.ts +40 -0
  66. package/dist/browser/PuppeteerNode.js +81 -0
  67. package/dist/browser/PuppeteerViewport.d.ts +5 -0
  68. package/dist/browser/PuppeteerViewport.js +2 -0
  69. package/dist/browser/ScreenshotOptions.d.ts +14 -0
  70. package/dist/browser/ScreenshotOptions.js +2 -0
  71. package/dist/browser/Target.d.ts +61 -0
  72. package/dist/browser/Target.js +146 -0
  73. package/dist/browser/TaskQueue.d.ts +20 -0
  74. package/dist/browser/TaskQueue.js +47 -0
  75. package/dist/browser/TimeoutSettings.d.ts +24 -0
  76. package/dist/browser/TimeoutSettings.js +62 -0
  77. package/dist/browser/assert.d.ts +1 -0
  78. package/dist/browser/assert.js +9 -0
  79. package/dist/browser/create-browser-fetcher.d.ts +17 -0
  80. package/dist/browser/create-browser-fetcher.js +119 -0
  81. package/dist/browser/devtools-commands.d.ts +270 -0
  82. package/dist/browser/devtools-commands.js +2 -0
  83. package/dist/browser/devtools-types.d.ts +1122 -0
  84. package/dist/browser/devtools-types.js +2 -0
  85. package/dist/browser/get-download-destination.d.ts +1 -0
  86. package/dist/browser/get-download-destination.js +38 -0
  87. package/dist/browser/mitt/index.d.ts +22 -0
  88. package/dist/browser/mitt/index.js +49 -0
  89. package/dist/browser/node.d.ts +2 -0
  90. package/dist/browser/node.js +9 -0
  91. package/dist/browser/revisions.d.ts +21 -0
  92. package/dist/browser/revisions.js +22 -0
  93. package/dist/browser/util.d.ts +47 -0
  94. package/dist/browser/util.js +169 -0
  95. package/dist/browser-executable.d.ts +1 -0
  96. package/dist/browser-executable.js +2 -0
  97. package/dist/browser-log.d.ts +1 -1
  98. package/dist/browser.d.ts +2 -0
  99. package/dist/browser.js +4 -0
  100. package/dist/calculate-ffmpeg-filters.d.ts +1 -1
  101. package/dist/calculate-ffmpeg-filters.js +2 -2
  102. package/dist/calculate-sar-dar-pixels.d.ts +9 -0
  103. package/dist/calculate-sar-dar-pixels.js +19 -0
  104. package/dist/can-use-parallel-encoding.d.ts +1 -1
  105. package/dist/can-use-parallel-encoding.js +2 -2
  106. package/dist/codec-supports-media.d.ts +8 -0
  107. package/dist/codec-supports-media.js +56 -0
  108. package/dist/codec.d.ts +4 -0
  109. package/dist/codec.js +16 -0
  110. package/dist/combine-videos.d.ts +4 -2
  111. package/dist/combine-videos.js +19 -5
  112. package/dist/compress-assets.d.ts +7 -0
  113. package/dist/compress-assets.js +25 -0
  114. package/dist/convert-number-of-gif-loops-to-ffmpeg.d.ts +1 -0
  115. package/dist/convert-number-of-gif-loops-to-ffmpeg.js +17 -0
  116. package/dist/convert-to-pcm.d.ts +1 -1
  117. package/dist/create-ffmpeg-complex-filter.d.ts +2 -1
  118. package/dist/create-ffmpeg-complex-filter.js +2 -11
  119. package/dist/create-ffmpeg-merge-filter.js +3 -3
  120. package/dist/create-silent-audio.d.ts +1 -1
  121. package/dist/crf.d.ts +5 -0
  122. package/dist/crf.js +64 -0
  123. package/dist/cycle-browser-tabs.d.ts +3 -2
  124. package/dist/cycle-browser-tabs.js +14 -2
  125. package/dist/delay-render-embedded-stack.d.ts +1 -1
  126. package/dist/ensure-frames-in-order.d.ts +1 -1
  127. package/dist/ensure-frames-in-order.js +3 -2
  128. package/dist/ensure-presentation-timestamp.d.ts +2 -0
  129. package/dist/ensure-presentation-timestamp.js +69 -0
  130. package/dist/error-handling/handle-javascript-exception.d.ts +2 -2
  131. package/dist/error-handling/handle-javascript-exception.js +3 -4
  132. package/dist/error-handling/symbolicate-error.d.ts +1 -1
  133. package/dist/error-handling/symbolicateable-error.d.ts +1 -1
  134. package/dist/extract-frame-from-video.d.ts +16 -0
  135. package/dist/extract-frame-from-video.js +277 -0
  136. package/dist/ffmpeg-executable.d.ts +1 -0
  137. package/dist/ffmpeg-executable.js +2 -0
  138. package/dist/ffmpeg-filter-file.d.ts +2 -1
  139. package/dist/ffmpeg-filter-file.js +4 -6
  140. package/dist/frame-range.d.ts +2 -0
  141. package/dist/frame-range.js +49 -0
  142. package/dist/frame-to-ffmpeg-timestamp.d.ts +1 -0
  143. package/dist/frame-to-ffmpeg-timestamp.js +8 -0
  144. package/dist/get-audio-codec-name.d.ts +1 -1
  145. package/dist/get-audio-codec-name.js +2 -2
  146. package/dist/get-browser-instance.d.ts +4 -3
  147. package/dist/get-browser-instance.js +2 -2
  148. package/dist/get-codec-name.d.ts +1 -1
  149. package/dist/get-codec-name.js +5 -2
  150. package/dist/get-compositions.d.ts +15 -5
  151. package/dist/get-compositions.js +35 -11
  152. package/dist/get-duration-from-frame-range.d.ts +1 -2
  153. package/dist/get-duration-from-frame-range.js +13 -9
  154. package/dist/get-extension-from-codec.d.ts +2 -2
  155. package/dist/get-extension-from-codec.js +5 -0
  156. package/dist/get-extension-of-filename.d.ts +1 -1
  157. package/dist/get-extension-of-filename.js +3 -0
  158. package/dist/get-frame-padded-index.d.ts +14 -0
  159. package/dist/get-frame-padded-index.js +33 -0
  160. package/dist/get-frame-to-render.d.ts +1 -1
  161. package/dist/get-local-browser-executable.d.ts +2 -1
  162. package/dist/get-local-browser-executable.js +7 -5
  163. package/dist/get-port.js +30 -37
  164. package/dist/get-prores-profile-name.d.ts +2 -1
  165. package/dist/get-video-info.d.ts +3 -0
  166. package/dist/get-video-info.js +49 -0
  167. package/dist/get-video-threads-flag.d.ts +1 -0
  168. package/dist/get-video-threads-flag.js +18 -0
  169. package/dist/guess-extension-for-media.d.ts +1 -0
  170. package/dist/guess-extension-for-media.js +27 -0
  171. package/dist/image-format.d.ts +6 -1
  172. package/dist/image-format.js +25 -1
  173. package/dist/index.d.ts +95 -20
  174. package/dist/index.js +93 -6
  175. package/dist/is-audio-codec.d.ts +2 -0
  176. package/dist/is-audio-codec.js +7 -0
  177. package/dist/is-beyond-last-frame.d.ts +3 -0
  178. package/dist/is-beyond-last-frame.js +12 -0
  179. package/dist/last-frame-from-video-cache.d.ts +17 -0
  180. package/dist/last-frame-from-video-cache.js +55 -0
  181. package/dist/log-level.d.ts +4 -0
  182. package/dist/log-level.js +15 -0
  183. package/dist/make-cancel-signal.d.ts +7 -0
  184. package/dist/make-cancel-signal.js +25 -0
  185. package/dist/merge-audio-track.d.ts +3 -1
  186. package/dist/merge-audio-track.js +14 -8
  187. package/dist/mime-db.d.ts +6 -0
  188. package/dist/mime-db.js +8636 -0
  189. package/dist/mime-types.d.ts +3 -0
  190. package/dist/mime-types.js +94 -0
  191. package/dist/offthread-video-server.d.ts +17 -0
  192. package/dist/offthread-video-server.js +86 -0
  193. package/dist/open-browser.d.ts +10 -7
  194. package/dist/open-browser.js +63 -20
  195. package/dist/overwrite.d.ts +1 -0
  196. package/dist/overwrite.js +4 -0
  197. package/dist/perf.d.ts +5 -0
  198. package/dist/perf.js +35 -0
  199. package/dist/pixel-format.d.ts +5 -0
  200. package/dist/pixel-format.js +26 -0
  201. package/dist/prepare-server.d.ts +14 -2
  202. package/dist/prepare-server.js +38 -5
  203. package/dist/preprocess-audio-track.d.ts +5 -2
  204. package/dist/preprocess-audio-track.js +3 -3
  205. package/dist/prespawn-ffmpeg.d.ts +7 -1
  206. package/dist/prespawn-ffmpeg.js +20 -16
  207. package/dist/prestitcher-memory-usage.d.ts +12 -0
  208. package/dist/prestitcher-memory-usage.js +30 -0
  209. package/dist/prores-profile.d.ts +5 -0
  210. package/dist/prores-profile.js +23 -0
  211. package/dist/provide-screenshot.d.ts +4 -4
  212. package/dist/provide-screenshot.js +1 -2
  213. package/dist/puppeteer-evaluate.d.ts +1 -1
  214. package/dist/puppeteer-evaluate.js +3 -4
  215. package/dist/puppeteer-screenshot.d.ts +3 -2
  216. package/dist/puppeteer-screenshot.js +7 -5
  217. package/dist/quality.d.ts +1 -0
  218. package/dist/quality.js +21 -0
  219. package/dist/render-frames.d.ts +24 -8
  220. package/dist/render-frames.js +153 -65
  221. package/dist/render-media.d.ts +36 -9
  222. package/dist/render-media.js +194 -70
  223. package/dist/render-still.d.ts +25 -7
  224. package/dist/render-still.js +91 -26
  225. package/dist/screenshot-dom-element.d.ts +6 -7
  226. package/dist/screenshot-dom-element.js +3 -6
  227. package/dist/screenshot-task.d.ts +3 -2
  228. package/dist/screenshot-task.js +36 -23
  229. package/dist/seek-to-frame.d.ts +2 -2
  230. package/dist/seek-to-frame.js +2 -2
  231. package/dist/serve-handler/index.d.ts +4 -0
  232. package/dist/serve-handler/index.js +204 -0
  233. package/dist/serve-handler/is-path-inside.d.ts +1 -0
  234. package/dist/serve-handler/is-path-inside.js +27 -0
  235. package/dist/serve-handler/range-parser.d.ts +13 -0
  236. package/dist/serve-handler/range-parser.js +57 -0
  237. package/dist/serve-static.d.ts +11 -3
  238. package/dist/serve-static.js +37 -7
  239. package/dist/set-props-and-env.d.ts +6 -2
  240. package/dist/set-props-and-env.js +65 -12
  241. package/dist/stitch-frames-to-video.d.ts +17 -5
  242. package/dist/stitch-frames-to-video.js +130 -46
  243. package/dist/stringify-ffmpeg-filter.d.ts +2 -2
  244. package/dist/stringify-ffmpeg-filter.js +12 -7
  245. package/dist/symbolicate-stacktrace.d.ts +1 -1
  246. package/dist/symbolicate-stacktrace.js +3 -3
  247. package/dist/tmp-dir.js +5 -1
  248. package/dist/truthy.d.ts +3 -0
  249. package/dist/truthy.js +7 -0
  250. package/dist/types.d.ts +1 -1
  251. package/dist/validate-concurrency.d.ts +1 -0
  252. package/dist/validate-concurrency.js +24 -0
  253. package/dist/validate-even-dimensions-with-codec.d.ts +1 -1
  254. package/dist/validate-even-dimensions-with-codec.js +2 -2
  255. package/dist/validate-every-nth-frame.d.ts +1 -0
  256. package/dist/validate-every-nth-frame.js +21 -0
  257. package/dist/validate-ffmpeg.js +2 -3
  258. package/dist/validate-frame.d.ts +1 -0
  259. package/dist/validate-frame.js +24 -0
  260. package/dist/validate-opengl-renderer.d.ts +5 -0
  261. package/dist/validate-opengl-renderer.js +15 -0
  262. package/dist/validate-output-filename.d.ts +1 -1
  263. package/dist/validate-output-filename.js +5 -0
  264. package/dist/wait-for-symbolication-error-to-be-done.d.ts +3 -0
  265. package/dist/wait-for-symbolication-error-to-be-done.js +34 -0
  266. package/dist/ws/ws-types.d.ts +14 -0
  267. package/dist/ws/ws-types.js +10 -0
  268. package/package.json +13 -16
  269. package/tsconfig.json +2 -2
  270. package/types/ws/index.d.ts +509 -0
  271. package/vitest.config.ts +8 -0
  272. package/dist/make-assets-download-dir.d.ts +0 -1
  273. package/dist/make-assets-download-dir.js +0 -8
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.extractFrameFromVideo = exports.getLastFrameOfVideo = void 0;
7
+ const execa_1 = __importDefault(require("execa"));
8
+ const get_video_stream_duration_1 = require("./assets/get-video-stream-duration");
9
+ const ensure_presentation_timestamp_1 = require("./ensure-presentation-timestamp");
10
+ const frame_to_ffmpeg_timestamp_1 = require("./frame-to-ffmpeg-timestamp");
11
+ const get_video_info_1 = require("./get-video-info");
12
+ const is_beyond_last_frame_1 = require("./is-beyond-last-frame");
13
+ const last_frame_from_video_cache_1 = require("./last-frame-from-video-cache");
14
+ const p_limit_1 = require("./p-limit");
15
+ const perf_1 = require("./perf");
16
+ const truthy_1 = require("./truthy");
17
+ const lastFrameLimit = (0, p_limit_1.pLimit)(1);
18
+ const mainLimit = (0, p_limit_1.pLimit)(5);
19
+ const determineVcodecFfmepgFlags = (vcodecFlag) => {
20
+ return [
21
+ vcodecFlag === 'vp9' ? '-vcodec' : null,
22
+ vcodecFlag === 'vp9' ? 'libvpx-vp9' : null,
23
+ vcodecFlag === 'vp8' ? '-vcodec' : null,
24
+ vcodecFlag === 'vp8' ? 'libvpx' : null,
25
+ ].filter(truthy_1.truthy);
26
+ };
27
+ const determineResizeParams = (needsResize) => {
28
+ if (needsResize === null) {
29
+ return [];
30
+ }
31
+ return ['-s', `${needsResize[0]}x${needsResize[1]}`];
32
+ };
33
+ // Uses no seeking, therefore the whole video has to be decoded. This is a last resort and should only happen
34
+ // if the video is corrupted
35
+ const getFrameOfVideoSlow = async ({ src, duration, ffmpegExecutable, imageFormat, specialVCodecForTransparency, needsResize, offset, fps, }) => {
36
+ console.warn(`\nUsing a slow method to extract the frame at ${duration}ms of ${src}. See https://remotion.dev/docs/slow-method-to-extract-frame for advice`);
37
+ const actualOffset = `-${duration * 1000 - offset}ms`;
38
+ const command = [
39
+ '-itsoffset',
40
+ actualOffset,
41
+ ...determineVcodecFfmepgFlags(specialVCodecForTransparency),
42
+ '-i',
43
+ src,
44
+ '-frames:v',
45
+ '1',
46
+ '-c:v',
47
+ imageFormat === 'jpeg' ? 'mjpeg' : 'png',
48
+ '-f',
49
+ 'image2pipe',
50
+ ...determineResizeParams(needsResize),
51
+ '-',
52
+ ].filter(truthy_1.truthy);
53
+ const { stdout, stderr } = (0, execa_1.default)(ffmpegExecutable !== null && ffmpegExecutable !== void 0 ? ffmpegExecutable : 'ffmpeg', command);
54
+ if (!stderr) {
55
+ throw new Error('unexpectedly did not get stderr');
56
+ }
57
+ if (!stdout) {
58
+ throw new Error('unexpectedly did not get stdout');
59
+ }
60
+ const stderrChunks = [];
61
+ const stdoutChunks = [];
62
+ const stdErrString = new Promise((resolve, reject) => {
63
+ stderr.on('data', (d) => stderrChunks.push(d));
64
+ stderr.on('error', (err) => reject(err));
65
+ stderr.on('end', () => resolve(Buffer.concat(stderrChunks).toString('utf-8')));
66
+ });
67
+ const stdoutChunk = new Promise((resolve, reject) => {
68
+ stdout.on('data', (d) => stdoutChunks.push(d));
69
+ stdout.on('error', (err) => reject(err));
70
+ stdout.on('end', () => resolve(Buffer.concat(stdoutChunks)));
71
+ });
72
+ const [stdErr, stdoutBuffer] = await Promise.all([stdErrString, stdoutChunk]);
73
+ const isEmpty = stdErr.includes('Output file is empty');
74
+ if (isEmpty) {
75
+ if (offset > 70) {
76
+ throw new Error(`Could not get last frame of ${src}. Tried to seek to the end using the command "ffmpeg ${command.join(' ')}" but got no frame. Most likely this video is corrupted.`);
77
+ }
78
+ return getFrameOfVideoSlow({
79
+ ffmpegExecutable,
80
+ duration,
81
+ // Decrement in 10ms increments, or 1 frame (e.g. fps = 25 --> 40ms)
82
+ offset: offset + (fps === null ? 10 : 1000 / fps),
83
+ src,
84
+ imageFormat,
85
+ specialVCodecForTransparency,
86
+ needsResize,
87
+ fps,
88
+ });
89
+ }
90
+ return stdoutBuffer;
91
+ };
92
+ const getLastFrameOfVideoFastUnlimited = async (options) => {
93
+ const { ffmpegExecutable, ffprobeExecutable, offset, src, downloadMap } = options;
94
+ const fromCache = (0, last_frame_from_video_cache_1.getLastFrameFromCache)({ ...options, offset: 0 });
95
+ if (fromCache) {
96
+ return fromCache;
97
+ }
98
+ const { duration, fps } = await (0, get_video_stream_duration_1.getVideoStreamDuration)(downloadMap, src, ffprobeExecutable);
99
+ if (duration === null) {
100
+ throw new Error(`Could not determine the duration of ${src} using FFMPEG. The file is not supported.`);
101
+ }
102
+ if (options.specialVCodecForTransparency === 'vp8' || offset > 40) {
103
+ const last = await getFrameOfVideoSlow({
104
+ duration,
105
+ ffmpegExecutable,
106
+ src,
107
+ imageFormat: options.imageFormat,
108
+ specialVCodecForTransparency: options.specialVCodecForTransparency,
109
+ needsResize: options.needsResize,
110
+ offset: offset - 1000 / (fps === null ? 10 : fps),
111
+ fps,
112
+ });
113
+ return last;
114
+ }
115
+ const actualOffset = `${duration * 1000 - offset}ms`;
116
+ const { stdout, stderr } = (0, execa_1.default)(ffmpegExecutable !== null && ffmpegExecutable !== void 0 ? ffmpegExecutable : 'ffmpeg', [
117
+ '-ss',
118
+ actualOffset,
119
+ ...determineVcodecFfmepgFlags(options.specialVCodecForTransparency),
120
+ '-i',
121
+ src,
122
+ '-frames:v',
123
+ '1',
124
+ '-c:v',
125
+ options.imageFormat === 'jpeg' ? 'mjpeg' : 'png',
126
+ '-f',
127
+ 'image2pipe',
128
+ ...determineResizeParams(options.needsResize),
129
+ '-',
130
+ ].filter(truthy_1.truthy));
131
+ if (!stderr) {
132
+ throw new Error('unexpectedly did not get stderr');
133
+ }
134
+ if (!stdout) {
135
+ throw new Error('unexpectedly did not get stdout');
136
+ }
137
+ const stderrChunks = [];
138
+ const stdoutChunks = [];
139
+ const stdErrString = new Promise((resolve, reject) => {
140
+ stderr.on('data', (d) => stderrChunks.push(d));
141
+ stderr.on('error', (err) => reject(err));
142
+ stderr.on('end', () => resolve(Buffer.concat(stderrChunks).toString('utf-8')));
143
+ });
144
+ const stdoutChunk = new Promise((resolve, reject) => {
145
+ stdout.on('data', (d) => {
146
+ stdoutChunks.push(d);
147
+ });
148
+ stdout.on('error', (err) => {
149
+ reject(err);
150
+ });
151
+ stdout.on('end', () => {
152
+ resolve(Buffer.concat(stdoutChunks));
153
+ });
154
+ });
155
+ const [stdErr, stdoutBuffer] = await Promise.all([stdErrString, stdoutChunk]);
156
+ const isEmpty = stdErr.includes('Output file is empty');
157
+ if (isEmpty) {
158
+ const unlimited = await getLastFrameOfVideoFastUnlimited({
159
+ ffmpegExecutable,
160
+ // Decrement in 10ms increments, or 1 frame (e.g. fps = 25 --> 40ms)
161
+ offset: offset + (fps === null ? 10 : 1000 / fps),
162
+ src,
163
+ ffprobeExecutable,
164
+ imageFormat: options.imageFormat,
165
+ specialVCodecForTransparency: options.specialVCodecForTransparency,
166
+ needsResize: options.needsResize,
167
+ downloadMap: options.downloadMap,
168
+ });
169
+ return unlimited;
170
+ }
171
+ return stdoutBuffer;
172
+ };
173
+ const getLastFrameOfVideo = async (options) => {
174
+ const result = await lastFrameLimit(getLastFrameOfVideoFastUnlimited, options);
175
+ (0, last_frame_from_video_cache_1.setLastFrameInCache)(options, result);
176
+ return result;
177
+ };
178
+ exports.getLastFrameOfVideo = getLastFrameOfVideo;
179
+ const extractFrameFromVideoFn = async ({ time, ffmpegExecutable, ffprobeExecutable, imageFormat, downloadMap, ...options }) => {
180
+ // We make a new copy of the video only for video because the conversion may affect
181
+ // audio rendering, so we work with 2 different files
182
+ const src = await (0, ensure_presentation_timestamp_1.ensurePresentationTimestamps)(downloadMap, options.src);
183
+ const { specialVcodec, needsResize } = await (0, get_video_info_1.getVideoInfo)(downloadMap, src, ffprobeExecutable);
184
+ if (specialVcodec === 'vp8') {
185
+ const { fps } = await (0, get_video_stream_duration_1.getVideoStreamDuration)(downloadMap, src, ffprobeExecutable);
186
+ return getFrameOfVideoSlow({
187
+ ffmpegExecutable,
188
+ imageFormat,
189
+ specialVCodecForTransparency: specialVcodec,
190
+ src,
191
+ duration: time,
192
+ needsResize,
193
+ offset: 0,
194
+ fps,
195
+ });
196
+ }
197
+ if ((0, is_beyond_last_frame_1.isBeyondLastFrame)(downloadMap, src, time)) {
198
+ const lastFrame = await (0, exports.getLastFrameOfVideo)({
199
+ ffmpegExecutable,
200
+ ffprobeExecutable,
201
+ offset: 0,
202
+ src,
203
+ imageFormat,
204
+ specialVCodecForTransparency: specialVcodec,
205
+ needsResize,
206
+ downloadMap,
207
+ });
208
+ return lastFrame;
209
+ }
210
+ const ffmpegTimestamp = (0, frame_to_ffmpeg_timestamp_1.frameToFfmpegTimestamp)(time);
211
+ const { stdout, stderr } = (0, execa_1.default)(ffmpegExecutable !== null && ffmpegExecutable !== void 0 ? ffmpegExecutable : 'ffmpeg', [
212
+ '-ss',
213
+ ffmpegTimestamp,
214
+ ...determineVcodecFfmepgFlags(specialVcodec),
215
+ '-i',
216
+ src,
217
+ '-frames:v',
218
+ '1',
219
+ '-f',
220
+ 'image2pipe',
221
+ '-vcodec',
222
+ imageFormat === 'jpeg' ? 'mjpeg' : 'png',
223
+ ...determineResizeParams(needsResize),
224
+ '-',
225
+ ].filter(truthy_1.truthy), {
226
+ buffer: false,
227
+ });
228
+ if (!stderr) {
229
+ throw new Error('unexpectedly did not get stderr');
230
+ }
231
+ if (!stdout) {
232
+ throw new Error('unexpectedly did not get stdout');
233
+ }
234
+ const stdoutChunks = [];
235
+ const stderrChunks = [];
236
+ const stderrStringProm = new Promise((resolve, reject) => {
237
+ stderr.on('data', (d) => stderrChunks.push(d));
238
+ stderr.on('error', (err) => reject(err));
239
+ stderr.on('end', () => resolve(Buffer.concat(stderrChunks).toString('utf8')));
240
+ });
241
+ const stdoutBuffer = new Promise((resolve, reject) => {
242
+ stdout.on('data', (d) => stdoutChunks.push(d));
243
+ stdout.on('error', (err) => reject(err));
244
+ stdout.on('end', () => resolve(Buffer.concat(stdoutChunks)));
245
+ });
246
+ const [stderrStr, stdOut] = await Promise.all([
247
+ stderrStringProm,
248
+ stdoutBuffer,
249
+ ]);
250
+ if (stderrStr.includes('Output file is empty')) {
251
+ (0, is_beyond_last_frame_1.markAsBeyondLastFrame)(downloadMap, src, time);
252
+ const last = await (0, exports.getLastFrameOfVideo)({
253
+ ffmpegExecutable,
254
+ ffprobeExecutable,
255
+ offset: 0,
256
+ src,
257
+ imageFormat,
258
+ specialVCodecForTransparency: specialVcodec,
259
+ needsResize,
260
+ downloadMap,
261
+ });
262
+ return last;
263
+ }
264
+ if (stdOut.length === 0) {
265
+ console.log('FFMPEG Logs:');
266
+ console.log(stderrStr);
267
+ throw new Error("Couldn't extract frame from video - FFMPEG did not return any data. Check logs to see more information");
268
+ }
269
+ return stdOut;
270
+ };
271
+ const extractFrameFromVideo = async (options) => {
272
+ const perf = (0, perf_1.startPerfMeasure)('extract-frame');
273
+ const res = await mainLimit(extractFrameFromVideoFn, options);
274
+ (0, perf_1.stopPerfMeasure)(perf);
275
+ return res;
276
+ };
277
+ exports.extractFrameFromVideo = extractFrameFromVideo;
@@ -0,0 +1 @@
1
+ export declare type FfmpegExecutable = string | null;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,4 +1,5 @@
1
- export declare const makeFfmpegFilterFile: (complexFilter: string) => Promise<{
1
+ import type { DownloadMap } from './assets/download-map';
2
+ export declare const makeFfmpegFilterFile: (complexFilter: string, downloadMap: DownloadMap) => Promise<{
2
3
  file: string;
3
4
  cleanup: () => void;
4
5
  }>;
@@ -8,16 +8,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.makeFfmpegFilterFile = void 0;
9
9
  const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
- const delete_directory_1 = require("./delete-directory");
12
- const tmp_dir_1 = require("./tmp-dir");
13
- const makeFfmpegFilterFile = async (complexFilter) => {
14
- const tempPath = (0, tmp_dir_1.tmpDir)('remotion-complex-filter');
15
- const filterFile = path_1.default.join(tempPath, 'complex-filter.txt');
11
+ const makeFfmpegFilterFile = async (complexFilter, downloadMap) => {
12
+ const random = Math.random().toString().replace('.', '');
13
+ const filterFile = path_1.default.join(downloadMap.complexFilter, 'complex-filter-' + random + '.txt');
16
14
  await fs_1.default.promises.writeFile(filterFile, complexFilter);
17
15
  return {
18
16
  file: filterFile,
19
17
  cleanup: () => {
20
- (0, delete_directory_1.deleteDirectory)(filterFile);
18
+ fs_1.default.unlinkSync(filterFile);
21
19
  },
22
20
  };
23
21
  };
@@ -0,0 +1,2 @@
1
+ export declare type FrameRange = number | [number, number];
2
+ export declare const validateFrameRange: (frameRange: FrameRange | null) => void;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateFrameRange = void 0;
4
+ const validateFrameRange = (frameRange) => {
5
+ if (frameRange === null) {
6
+ return;
7
+ }
8
+ if (typeof frameRange === 'number') {
9
+ if (frameRange < 0) {
10
+ throw new TypeError('Frame must be a non-negative number, got ' + frameRange);
11
+ }
12
+ if (!Number.isFinite(frameRange)) {
13
+ throw new TypeError('Frame must be a finite number, got ' + frameRange);
14
+ }
15
+ if (!Number.isInteger(frameRange)) {
16
+ throw new Error(`Frame must be an integer, but got a float (${frameRange})`);
17
+ }
18
+ return;
19
+ }
20
+ if (Array.isArray(frameRange)) {
21
+ if (frameRange.length !== 2) {
22
+ throw new TypeError('Frame range must be a tuple, got an array with length ' +
23
+ frameRange.length);
24
+ }
25
+ for (const value of frameRange) {
26
+ if (typeof value !== 'number') {
27
+ throw new Error(`Each value of frame range must be a number, but got ${typeof value} (${JSON.stringify(value)})`);
28
+ }
29
+ if (!Number.isFinite(value)) {
30
+ throw new TypeError('Each value of frame range must be finite, but got ' + value);
31
+ }
32
+ if (!Number.isInteger(value)) {
33
+ throw new Error(`Each value of frame range must be an integer, but got a float (${value})`);
34
+ }
35
+ if (value < 0) {
36
+ throw new Error(`Each value of frame range must be non-negative, but got ${value}`);
37
+ }
38
+ }
39
+ const [first, second] = frameRange;
40
+ if (second < first) {
41
+ throw new Error('The second value of frame range must be not smaller than the first one, but got ' +
42
+ frameRange.join('-'));
43
+ }
44
+ return;
45
+ }
46
+ throw new TypeError('Frame range must be a number or a tuple of numbers, but got object of type ' +
47
+ typeof frameRange);
48
+ };
49
+ exports.validateFrameRange = validateFrameRange;
@@ -0,0 +1 @@
1
+ export declare const frameToFfmpegTimestamp: (time: number) => string;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.frameToFfmpegTimestamp = void 0;
4
+ const frameToFfmpegTimestamp = (time) => {
5
+ // We ceil because FFMPEG seeks to the closest frame BEFORE the position
6
+ return (Math.ceil(time * 1000000) / 1000).toFixed(3) + 'ms';
7
+ };
8
+ exports.frameToFfmpegTimestamp = frameToFfmpegTimestamp;
@@ -1,2 +1,2 @@
1
- import { Codec } from 'remotion';
1
+ import type { Codec } from './codec';
2
2
  export declare const getAudioCodecName: (codec: Codec) => string | null;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getAudioCodecName = void 0;
4
- const remotion_1 = require("remotion");
4
+ const is_audio_codec_1 = require("./is-audio-codec");
5
5
  const getAudioCodecName = (codec) => {
6
- if (!remotion_1.Internals.isAudioCodec(codec)) {
6
+ if (!(0, is_audio_codec_1.isAudioCodec)(codec)) {
7
7
  // The mkv container supports WAV, but MP4 does only support
8
8
  // AAC. Choose MKV codec for better quality because we can put in lossless audio
9
9
  if (codec === 'h264-mkv') {
@@ -1,6 +1,7 @@
1
- import { Browser, Page } from 'puppeteer-core';
2
- import { BrowserExecutable } from 'remotion';
3
- import { ChromiumOptions } from './open-browser';
1
+ import type { BrowserExecutable } from './browser-executable';
2
+ import type { Browser } from './browser/Browser';
3
+ import type { Page } from './browser/BrowserPage';
4
+ import type { ChromiumOptions } from './open-browser';
4
5
  export declare const getPageAndCleanupFn: ({ passedInInstance, browserExecutable, chromiumOptions, }: {
5
6
  passedInInstance: Browser | undefined;
6
7
  browserExecutable: BrowserExecutable | null;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getPageAndCleanupFn = void 0;
4
- const remotion_1 = require("remotion");
4
+ const browser_1 = require("./browser");
5
5
  const open_browser_1 = require("./open-browser");
6
6
  const getPageAndCleanupFn = async ({ passedInInstance, browserExecutable, chromiumOptions, }) => {
7
7
  if (passedInInstance) {
@@ -17,7 +17,7 @@ const getPageAndCleanupFn = async ({ passedInInstance, browserExecutable, chromi
17
17
  },
18
18
  };
19
19
  }
20
- const browserInstance = await (0, open_browser_1.openBrowser)(remotion_1.Internals.DEFAULT_BROWSER, {
20
+ const browserInstance = await (0, open_browser_1.openBrowser)(browser_1.DEFAULT_BROWSER, {
21
21
  browserExecutable,
22
22
  chromiumOptions,
23
23
  });
@@ -1,2 +1,2 @@
1
- import { Codec } from 'remotion';
1
+ import type { Codec } from './codec';
2
2
  export declare const getCodecName: (codec: Codec) => string | null;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCodecName = void 0;
4
- const remotion_1 = require("remotion");
4
+ const is_audio_codec_1 = require("./is-audio-codec");
5
5
  const getCodecName = (codec) => {
6
- if (remotion_1.Internals.isAudioCodec(codec)) {
6
+ if ((0, is_audio_codec_1.isAudioCodec)(codec)) {
7
7
  return null;
8
8
  }
9
9
  if (codec === 'h264' || codec === 'h264-mkv') {
@@ -21,6 +21,9 @@ const getCodecName = (codec) => {
21
21
  if (codec === 'prores') {
22
22
  return 'prores_ks';
23
23
  }
24
+ if (codec === 'gif') {
25
+ return 'gif';
26
+ }
24
27
  throw new TypeError(`Cannot find FFMPEG codec for ${codec}`);
25
28
  };
26
29
  exports.getCodecName = getCodecName;
@@ -1,7 +1,10 @@
1
- import { Browser } from 'puppeteer-core';
2
- import { BrowserExecutable, TCompMetadata } from 'remotion';
3
- import { BrowserLog } from './browser-log';
4
- import { ChromiumOptions } from './open-browser';
1
+ import type { TCompMetadata } from 'remotion';
2
+ import type { DownloadMap } from './assets/download-map';
3
+ import type { BrowserExecutable } from './browser-executable';
4
+ import type { BrowserLog } from './browser-log';
5
+ import type { Browser } from './browser/Browser';
6
+ import type { FfmpegExecutable } from './ffmpeg-executable';
7
+ import type { ChromiumOptions } from './open-browser';
5
8
  declare type GetCompositionsConfig = {
6
9
  inputProps?: object | null;
7
10
  envVariables?: Record<string, string>;
@@ -10,6 +13,13 @@ declare type GetCompositionsConfig = {
10
13
  browserExecutable?: BrowserExecutable;
11
14
  timeoutInMilliseconds?: number;
12
15
  chromiumOptions?: ChromiumOptions;
16
+ ffmpegExecutable?: FfmpegExecutable;
17
+ ffprobeExecutable?: FfmpegExecutable;
18
+ port?: number | null;
19
+ /**
20
+ * @deprecated Only for Remotion internal usage
21
+ */
22
+ downloadMap?: DownloadMap;
13
23
  };
14
- export declare const getCompositions: (serveUrlOrWebpackUrl: string, config?: GetCompositionsConfig | undefined) => Promise<TCompMetadata[]>;
24
+ export declare const getCompositions: (serveUrlOrWebpackUrl: string, config?: GetCompositionsConfig) => Promise<TCompMetadata[]>;
15
25
  export {};
@@ -1,20 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCompositions = void 0;
4
+ const download_map_1 = require("./assets/download-map");
4
5
  const handle_javascript_exception_1 = require("./error-handling/handle-javascript-exception");
5
6
  const get_browser_instance_1 = require("./get-browser-instance");
6
7
  const prepare_server_1 = require("./prepare-server");
7
8
  const puppeteer_evaluate_1 = require("./puppeteer-evaluate");
8
9
  const set_props_and_env_1 = require("./set-props-and-env");
9
10
  const validate_puppeteer_timeout_1 = require("./validate-puppeteer-timeout");
10
- const innerGetCompositions = async (serveUrl, page, config) => {
11
+ const innerGetCompositions = async (serveUrl, page, config, proxyPort) => {
11
12
  if (config === null || config === void 0 ? void 0 : config.onBrowserLog) {
12
13
  page.on('console', (log) => {
13
14
  var _a;
14
15
  (_a = config.onBrowserLog) === null || _a === void 0 ? void 0 : _a.call(config, {
15
16
  stackTrace: log.stackTrace(),
16
- text: log.text(),
17
- type: log.type(),
17
+ text: log.text,
18
+ type: log.type,
18
19
  });
19
20
  });
20
21
  }
@@ -26,6 +27,10 @@ const innerGetCompositions = async (serveUrl, page, config) => {
26
27
  serveUrl,
27
28
  initialFrame: 0,
28
29
  timeoutInMilliseconds: config === null || config === void 0 ? void 0 : config.timeoutInMilliseconds,
30
+ proxyPort,
31
+ retriesRemaining: 2,
32
+ audioEnabled: false,
33
+ videoEnabled: false,
29
34
  });
30
35
  await (0, puppeteer_evaluate_1.puppeteerEvaluateWithCatch)({
31
36
  page,
@@ -37,7 +42,7 @@ const innerGetCompositions = async (serveUrl, page, config) => {
37
42
  frame: null,
38
43
  args: [],
39
44
  });
40
- await page.waitForFunction('window.ready === true');
45
+ await page.waitForFunction(page.browser, 'window.ready === true');
41
46
  const result = await (0, puppeteer_evaluate_1.puppeteerEvaluateWithCatch)({
42
47
  pageFunction: () => {
43
48
  return window.getStaticCompositions();
@@ -49,28 +54,47 @@ const innerGetCompositions = async (serveUrl, page, config) => {
49
54
  return result;
50
55
  };
51
56
  const getCompositions = async (serveUrlOrWebpackUrl, config) => {
52
- var _a, _b;
53
- const { serveUrl, closeServer } = await (0, prepare_server_1.prepareServer)(serveUrlOrWebpackUrl);
57
+ var _a, _b, _c;
58
+ const downloadMap = (_a = config === null || config === void 0 ? void 0 : config.downloadMap) !== null && _a !== void 0 ? _a : (0, download_map_1.makeDownloadMap)();
54
59
  const { page, cleanup } = await (0, get_browser_instance_1.getPageAndCleanupFn)({
55
60
  passedInInstance: config === null || config === void 0 ? void 0 : config.puppeteerInstance,
56
- browserExecutable: (_a = config === null || config === void 0 ? void 0 : config.browserExecutable) !== null && _a !== void 0 ? _a : null,
57
- chromiumOptions: (_b = config === null || config === void 0 ? void 0 : config.chromiumOptions) !== null && _b !== void 0 ? _b : {},
61
+ browserExecutable: (_b = config === null || config === void 0 ? void 0 : config.browserExecutable) !== null && _b !== void 0 ? _b : null,
62
+ chromiumOptions: (_c = config === null || config === void 0 ? void 0 : config.chromiumOptions) !== null && _c !== void 0 ? _c : {},
58
63
  });
59
64
  return new Promise((resolve, reject) => {
65
+ var _a, _b, _c;
66
+ const onError = (err) => reject(err);
60
67
  const cleanupPageError = (0, handle_javascript_exception_1.handleJavascriptException)({
61
68
  page,
62
69
  frame: null,
63
- onError: (err) => reject(err),
70
+ onError,
64
71
  });
65
- innerGetCompositions(serveUrl, page, config !== null && config !== void 0 ? config : {})
72
+ let close = null;
73
+ (0, prepare_server_1.prepareServer)({
74
+ webpackConfigOrServeUrl: serveUrlOrWebpackUrl,
75
+ onDownload: () => undefined,
76
+ onError,
77
+ ffmpegExecutable: (_a = config === null || config === void 0 ? void 0 : config.ffmpegExecutable) !== null && _a !== void 0 ? _a : null,
78
+ ffprobeExecutable: (_b = config === null || config === void 0 ? void 0 : config.ffprobeExecutable) !== null && _b !== void 0 ? _b : null,
79
+ port: (_c = config === null || config === void 0 ? void 0 : config.port) !== null && _c !== void 0 ? _c : null,
80
+ downloadMap,
81
+ })
82
+ .then(({ serveUrl, closeServer, offthreadPort }) => {
83
+ close = closeServer;
84
+ return innerGetCompositions(serveUrl, page, config !== null && config !== void 0 ? config : {}, offthreadPort);
85
+ })
66
86
  .then((comp) => resolve(comp))
67
87
  .catch((err) => {
68
88
  reject(err);
69
89
  })
70
90
  .finally(() => {
71
91
  cleanup();
72
- closeServer();
92
+ close === null || close === void 0 ? void 0 : close();
73
93
  cleanupPageError();
94
+ // Clean download map if it was not passed in
95
+ if (!(config === null || config === void 0 ? void 0 : config.downloadMap)) {
96
+ (0, download_map_1.cleanDownloadMap)(downloadMap);
97
+ }
74
98
  });
75
99
  });
76
100
  };
@@ -1,2 +1 @@
1
- import { FrameRange } from 'remotion';
2
- export declare const getDurationFromFrameRange: (frameRange: FrameRange | null, durationInFrames: number) => number;
1
+ export declare const getFramesToRender: (frameRange: [number, number], everyNthFrame: number) => number[];
@@ -1,13 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getDurationFromFrameRange = void 0;
4
- const getDurationFromFrameRange = (frameRange, durationInFrames) => {
5
- if (frameRange === null) {
6
- return durationInFrames;
3
+ exports.getFramesToRender = void 0;
4
+ const getFramesToRender = (frameRange, everyNthFrame) => {
5
+ if (everyNthFrame === 0) {
6
+ throw new Error('everyNthFrame cannot be 0');
7
7
  }
8
- if (typeof frameRange === 'number') {
9
- return 1;
10
- }
11
- return frameRange[1] - frameRange[0] + 1;
8
+ return new Array(frameRange[1] - frameRange[0] + 1)
9
+ .fill(true)
10
+ .map((_, index) => {
11
+ return index + frameRange[0];
12
+ })
13
+ .filter((index) => {
14
+ return index % everyNthFrame === 0;
15
+ });
12
16
  };
13
- exports.getDurationFromFrameRange = getDurationFromFrameRange;
17
+ exports.getFramesToRender = getFramesToRender;
@@ -1,2 +1,2 @@
1
- import { Codec } from 'remotion';
2
- export declare const getFileExtensionFromCodec: (codec: Codec, type: 'chunk' | 'final') => "mp3" | "aac" | "wav" | "mp4" | "mkv" | "mov" | "webm";
1
+ import type { Codec } from './codec';
2
+ export declare const getFileExtensionFromCodec: (codec: Codec, type: 'chunk' | 'final') => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
@@ -23,6 +23,11 @@ const getFileExtensionFromCodec = (codec, type) => {
23
23
  return 'webm';
24
24
  case 'vp9':
25
25
  return 'webm';
26
+ case 'gif':
27
+ if (type === 'chunk') {
28
+ return 'mkv';
29
+ }
30
+ return 'gif';
26
31
  case 'wav':
27
32
  return 'wav';
28
33
  default:
@@ -1 +1 @@
1
- export declare const getExtensionOfFilename: (filename: string) => string | null;
1
+ export declare const getExtensionOfFilename: (filename: string | null) => string | null;
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getExtensionOfFilename = void 0;
4
4
  const getExtensionOfFilename = (filename) => {
5
+ if (filename === null) {
6
+ return null;
7
+ }
5
8
  const filenameArr = filename.split('.');
6
9
  const hasExtension = filenameArr.length >= 2;
7
10
  const filenameArrLength = filenameArr.length;