jbilcke-hf HF staff commited on
Commit
4cb7ad9
·
1 Parent(s): 89f3b87

make the format a parameter

Browse files
package-lock.json CHANGED
@@ -21,6 +21,7 @@
21
  "mime-types": "^2.1.35",
22
  "node-fetch": "^3.3.1",
23
  "puppeteer": "^22.7.0",
 
24
  "sharp": "^0.33.3",
25
  "temp-dir": "^3.0.0",
26
  "ts-node": "^10.9.1",
@@ -1543,6 +1544,14 @@
1543
  "ms": "2.0.0"
1544
  }
1545
  },
 
 
 
 
 
 
 
 
1546
  "node_modules/define-data-property": {
1547
  "version": "1.1.4",
1548
  "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
@@ -1921,6 +1930,17 @@
1921
  "node": "^12.20 || >= 14.13"
1922
  }
1923
  },
 
 
 
 
 
 
 
 
 
 
 
1924
  "node_modules/finalhandler": {
1925
  "version": "1.2.0",
1926
  "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
@@ -2789,6 +2809,22 @@
2789
  "url": "https://github.com/sponsors/ljharb"
2790
  }
2791
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2792
  "node_modules/queue-tick": {
2793
  "version": "1.0.1",
2794
  "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
@@ -3087,6 +3123,17 @@
3087
  "node": ">=0.10.0"
3088
  }
3089
  },
 
 
 
 
 
 
 
 
 
 
 
3090
  "node_modules/sprintf-js": {
3091
  "version": "1.1.3",
3092
  "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
 
21
  "mime-types": "^2.1.35",
22
  "node-fetch": "^3.3.1",
23
  "puppeteer": "^22.7.0",
24
+ "query-string": "^9.0.0",
25
  "sharp": "^0.33.3",
26
  "temp-dir": "^3.0.0",
27
  "ts-node": "^10.9.1",
 
1544
  "ms": "2.0.0"
1545
  }
1546
  },
1547
+ "node_modules/decode-uri-component": {
1548
+ "version": "0.4.1",
1549
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
1550
+ "integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==",
1551
+ "engines": {
1552
+ "node": ">=14.16"
1553
+ }
1554
+ },
1555
  "node_modules/define-data-property": {
1556
  "version": "1.1.4",
1557
  "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
 
1930
  "node": "^12.20 || >= 14.13"
1931
  }
1932
  },
1933
+ "node_modules/filter-obj": {
1934
+ "version": "5.1.0",
1935
+ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz",
1936
+ "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==",
1937
+ "engines": {
1938
+ "node": ">=14.16"
1939
+ },
1940
+ "funding": {
1941
+ "url": "https://github.com/sponsors/sindresorhus"
1942
+ }
1943
+ },
1944
  "node_modules/finalhandler": {
1945
  "version": "1.2.0",
1946
  "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
 
2809
  "url": "https://github.com/sponsors/ljharb"
2810
  }
2811
  },
2812
+ "node_modules/query-string": {
2813
+ "version": "9.0.0",
2814
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.0.0.tgz",
2815
+ "integrity": "sha512-4EWwcRGsO2H+yzq6ddHcVqkCQ2EFUSfDMEjF8ryp8ReymyZhIuaFRGLomeOQLkrzacMHoyky2HW0Qe30UbzkKw==",
2816
+ "dependencies": {
2817
+ "decode-uri-component": "^0.4.1",
2818
+ "filter-obj": "^5.1.0",
2819
+ "split-on-first": "^3.0.0"
2820
+ },
2821
+ "engines": {
2822
+ "node": ">=18"
2823
+ },
2824
+ "funding": {
2825
+ "url": "https://github.com/sponsors/sindresorhus"
2826
+ }
2827
+ },
2828
  "node_modules/queue-tick": {
2829
  "version": "1.0.1",
2830
  "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
 
3123
  "node": ">=0.10.0"
3124
  }
3125
  },
3126
+ "node_modules/split-on-first": {
3127
+ "version": "3.0.0",
3128
+ "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz",
3129
+ "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==",
3130
+ "engines": {
3131
+ "node": ">=12"
3132
+ },
3133
+ "funding": {
3134
+ "url": "https://github.com/sponsors/sindresorhus"
3135
+ }
3136
+ },
3137
  "node_modules/sprintf-js": {
3138
  "version": "1.1.3",
3139
  "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
package.json CHANGED
@@ -26,6 +26,7 @@
26
  "mime-types": "^2.1.35",
27
  "node-fetch": "^3.3.1",
28
  "puppeteer": "^22.7.0",
 
29
  "sharp": "^0.33.3",
30
  "temp-dir": "^3.0.0",
31
  "ts-node": "^10.9.1",
 
26
  "mime-types": "^2.1.35",
27
  "node-fetch": "^3.3.1",
28
  "puppeteer": "^22.7.0",
29
+ "query-string": "^9.0.0",
30
  "sharp": "^0.33.3",
31
  "temp-dir": "^3.0.0",
32
  "ts-node": "^10.9.1",
src/core/ffmpeg/concatenateVideosWithAudio.mts CHANGED
@@ -10,8 +10,12 @@ import { getMediaInfo } from "./getMediaInfo.mts";
10
  import { removeTemporaryFiles } from "../files/removeTmpFiles.mts";
11
  import { addBase64Header } from "../base64/addBase64.mts";
12
 
13
- type ConcatenateVideoWithAudioOptions = {
 
 
 
14
  output?: string;
 
15
  audioTrack?: string; // base64
16
  audioFilePath?: string; // path
17
  videoTracks?: string[]; // base64
@@ -24,6 +28,7 @@ type ConcatenateVideoWithAudioOptions = {
24
 
25
  export const concatenateVideosWithAudio = async ({
26
  output,
 
27
  audioTrack = "",
28
  audioFilePath = "",
29
  videoTracks = [],
@@ -65,7 +70,7 @@ export const concatenateVideosWithAudio = async ({
65
  const tempMediaInfo = await getMediaInfo(tempFilePath.filepath);
66
  const hasOriginalAudio = tempMediaInfo.hasAudio;
67
 
68
- const finalOutputFilePath = output || path.join(tempDir, `${uuidv4()}.mp4`);
69
 
70
  // Begin ffmpeg command configuration
71
  let cmd = ffmpeg();
@@ -133,7 +138,7 @@ export const concatenateVideosWithAudio = async ({
133
  if (asBase64) {
134
  try {
135
  const outputBuffer = await fs.readFile(finalOutputFilePath);
136
- const outputBase64 = addBase64Header(outputBuffer.toString("base64"), "mp4")
137
  resolve(outputBase64);
138
  } catch (error) {
139
  reject(new Error(`Error reading output video file: ${(error as Error).message}`));
 
10
  import { removeTemporaryFiles } from "../files/removeTmpFiles.mts";
11
  import { addBase64Header } from "../base64/addBase64.mts";
12
 
13
+ export type SupportedExportFormat = "mp4" | "webm"
14
+ export const defaultExportFormat = "mp4"
15
+
16
+ export type ConcatenateVideoWithAudioOptions = {
17
  output?: string;
18
+ format?: SupportedExportFormat;
19
  audioTrack?: string; // base64
20
  audioFilePath?: string; // path
21
  videoTracks?: string[]; // base64
 
28
 
29
  export const concatenateVideosWithAudio = async ({
30
  output,
31
+ format = defaultExportFormat,
32
  audioTrack = "",
33
  audioFilePath = "",
34
  videoTracks = [],
 
70
  const tempMediaInfo = await getMediaInfo(tempFilePath.filepath);
71
  const hasOriginalAudio = tempMediaInfo.hasAudio;
72
 
73
+ const finalOutputFilePath = output || path.join(tempDir, `${uuidv4()}.${format}`);
74
 
75
  // Begin ffmpeg command configuration
76
  let cmd = ffmpeg();
 
138
  if (asBase64) {
139
  try {
140
  const outputBuffer = await fs.readFile(finalOutputFilePath);
141
+ const outputBase64 = addBase64Header(outputBuffer.toString("base64"), format)
142
  resolve(outputBase64);
143
  } catch (error) {
144
  reject(new Error(`Error reading output video file: ${(error as Error).message}`));
src/index.mts CHANGED
@@ -6,6 +6,8 @@ import { parseClap, ClapProject } from "@aitube/clap"
6
 
7
  import { clapToTmpVideoFilePath } from "./main.mts"
8
  import { deleteFile } from "./core/files/deleteFile.mts"
 
 
9
 
10
  const app = express()
11
  const port = 7860
@@ -39,9 +41,21 @@ app.get("/", async (req, res) => {
39
  res.end()
40
  })
41
 
 
42
  // the export robot has only one job: to export .clap files
43
  app.post("/", async (req, res) => {
44
 
 
 
 
 
 
 
 
 
 
 
 
45
  let data: Uint8Array[] = [];
46
 
47
  req.on("data", (chunk) => {
@@ -59,8 +73,8 @@ app.post("/", async (req, res) => {
59
  const {
60
  tmpWorkDir,
61
  outputFilePath,
62
- } = await clapToTmpVideoFilePath({ clap })
63
- console.log("got an output file at:", outputFilePath)
64
 
65
  res.download(outputFilePath, async () => {
66
  // clean-up after ourselves (we clear the whole tmp directory)
 
6
 
7
  import { clapToTmpVideoFilePath } from "./main.mts"
8
  import { deleteFile } from "./core/files/deleteFile.mts"
9
+ import queryString from "query-string"
10
+ import { defaultExportFormat, SupportedExportFormat } from "./core/ffmpeg/concatenateVideosWithAudio.mts"
11
 
12
  const app = express()
13
  const port = 7860
 
41
  res.end()
42
  })
43
 
44
+
45
  // the export robot has only one job: to export .clap files
46
  app.post("/", async (req, res) => {
47
 
48
+ const qs = queryString.parseUrl(req.url || "")
49
+ const query = (qs || {}).query
50
+
51
+ let format: SupportedExportFormat = defaultExportFormat
52
+ try {
53
+ format = decodeURIComponent(query?.f?.toString() || defaultExportFormat).trim() as SupportedExportFormat
54
+ if (format !== "mp4" && format !== "webm") {
55
+ format = defaultExportFormat
56
+ }
57
+ } catch (err) {}
58
+
59
  let data: Uint8Array[] = [];
60
 
61
  req.on("data", (chunk) => {
 
73
  const {
74
  tmpWorkDir,
75
  outputFilePath,
76
+ } = await clapToTmpVideoFilePath({ clap, format })
77
+ console.log(`got an output ${format} file at:`, outputFilePath)
78
 
79
  res.download(outputFilePath, async () => {
80
  // clean-up after ourselves (we clear the whole tmp directory)
src/main.mts CHANGED
@@ -3,7 +3,7 @@ import { join } from "node:path"
3
  import { ClapProject } from "@aitube/clap";
4
 
5
  import { concatenateAudio } from "./core/ffmpeg/concatenateAudio.mts";
6
- import { concatenateVideosWithAudio } from "./core/ffmpeg/concatenateVideosWithAudio.mts";
7
  import { writeBase64ToFile } from "./core/files/writeBase64ToFile.mts";
8
  import { concatenateVideos } from "./core/ffmpeg/concatenateVideos.mts"
9
  import { deleteFilesWithName } from "./core/files/deleteFileWithName.mts"
@@ -11,6 +11,7 @@ import { getRandomDirectory } from "./core/files/getRandomDirectory.mts";
11
  import { clapWithVideosToVideoFile } from "./core/exporters/clapWithVideosToVideoFile.mts";
12
  import { clapWithStoryboardsToVideoFile } from "./core/exporters/clapWithStoryboardsToVideoFile.mts";
13
 
 
14
  /**
15
  * Generate a .mp4 video inside a directory (if none is provided, it will be created in /tmp)
16
  *
@@ -19,11 +20,13 @@ import { clapWithStoryboardsToVideoFile } from "./core/exporters/clapWithStorybo
19
  */
20
  export async function clapToTmpVideoFilePath({
21
  clap,
 
22
  outputDir = "",
23
  clearTmpFilesAtEnd = false
24
  }: {
25
  clap: ClapProject
26
 
 
27
  outputDir?: string
28
 
29
  // if you leave this to false, you will have to clear files yourself
@@ -90,7 +93,8 @@ export async function clapToTmpVideoFilePath({
90
  })
91
 
92
  const finalFilePathOfVideoWithMusic = await concatenateVideosWithAudio({
93
- output: join(outputDir, `final_video.mp4`),
 
94
  audioFilePath: concatenatedAudio.filepath,
95
  videoFilePaths: [concatenatedVideosNoMusic.filepath],
96
  // videos are silent, so they can stay at 0
 
3
  import { ClapProject } from "@aitube/clap";
4
 
5
  import { concatenateAudio } from "./core/ffmpeg/concatenateAudio.mts";
6
+ import { concatenateVideosWithAudio, defaultExportFormat, SupportedExportFormat } from "./core/ffmpeg/concatenateVideosWithAudio.mts";
7
  import { writeBase64ToFile } from "./core/files/writeBase64ToFile.mts";
8
  import { concatenateVideos } from "./core/ffmpeg/concatenateVideos.mts"
9
  import { deleteFilesWithName } from "./core/files/deleteFileWithName.mts"
 
11
  import { clapWithVideosToVideoFile } from "./core/exporters/clapWithVideosToVideoFile.mts";
12
  import { clapWithStoryboardsToVideoFile } from "./core/exporters/clapWithStoryboardsToVideoFile.mts";
13
 
14
+
15
  /**
16
  * Generate a .mp4 video inside a directory (if none is provided, it will be created in /tmp)
17
  *
 
20
  */
21
  export async function clapToTmpVideoFilePath({
22
  clap,
23
+ format = defaultExportFormat,
24
  outputDir = "",
25
  clearTmpFilesAtEnd = false
26
  }: {
27
  clap: ClapProject
28
 
29
+ format?: SupportedExportFormat
30
  outputDir?: string
31
 
32
  // if you leave this to false, you will have to clear files yourself
 
93
  })
94
 
95
  const finalFilePathOfVideoWithMusic = await concatenateVideosWithAudio({
96
+ output: join(outputDir, `final_video.${format}`),
97
+ format,
98
  audioFilePath: concatenatedAudio.filepath,
99
  videoFilePaths: [concatenatedVideosNoMusic.filepath],
100
  // videos are silent, so they can stay at 0