jbilcke-hf HF staff commited on
Commit
846ea5b
1 Parent(s): 989fb3a

project is starting to take shape

Browse files
package-lock.json CHANGED
@@ -9,7 +9,7 @@
9
  "version": "0.0.0",
10
  "dependencies": {
11
  "@aitube/clap": "^0.0.6",
12
- "@aitube/client": "^0.0.3",
13
  "@radix-ui/react-accordion": "^1.1.2",
14
  "@radix-ui/react-avatar": "^1.0.4",
15
  "@radix-ui/react-checkbox": "^1.0.4",
@@ -39,6 +39,7 @@
39
  "eslint": "8.56.0",
40
  "eslint-config-next": "14.1.0",
41
  "jimp": "^0.22.12",
 
42
  "lucide-react": "^0.334.0",
43
  "next": "^14.2.3",
44
  "next-themes": "^0.2.1",
@@ -60,14 +61,6 @@
60
  "zustand": "^4.5.2"
61
  }
62
  },
63
- "node_modules/@aashutoshrathi/word-wrap": {
64
- "version": "1.2.6",
65
- "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
66
- "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
67
- "engines": {
68
- "node": ">=0.10.0"
69
- }
70
- },
71
  "node_modules/@aitube/clap": {
72
  "version": "0.0.6",
73
  "resolved": "https://registry.npmjs.org/@aitube/clap/-/clap-0.0.6.tgz",
@@ -81,9 +74,9 @@
81
  }
82
  },
83
  "node_modules/@aitube/client": {
84
- "version": "0.0.3",
85
- "resolved": "https://registry.npmjs.org/@aitube/client/-/client-0.0.3.tgz",
86
- "integrity": "sha512-BO6CVyNP3kqRJbVmecF2YZlmKyePGIdI6wcSzEkcIEgZTqI1D6oO7AZqbBIA8JhQBQTwuAYO6DhoYqclnG0J8w==",
87
  "dependencies": {
88
  "uuid": "^9.0.1",
89
  "yaml": "^2.4.1"
@@ -5315,6 +5308,14 @@
5315
  "jiti": "bin/jiti.js"
5316
  }
5317
  },
 
 
 
 
 
 
 
 
5318
  "node_modules/jpeg-js": {
5319
  "version": "0.4.4",
5320
  "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
@@ -5863,16 +5864,16 @@
5863
  }
5864
  },
5865
  "node_modules/optionator": {
5866
- "version": "0.9.3",
5867
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
5868
- "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
5869
  "dependencies": {
5870
- "@aashutoshrathi/word-wrap": "^1.2.3",
5871
  "deep-is": "^0.1.3",
5872
  "fast-levenshtein": "^2.0.6",
5873
  "levn": "^0.4.1",
5874
  "prelude-ls": "^1.2.1",
5875
- "type-check": "^0.4.0"
 
5876
  },
5877
  "engines": {
5878
  "node": ">= 0.8.0"
@@ -7665,6 +7666,14 @@
7665
  "url": "https://github.com/sponsors/ljharb"
7666
  }
7667
  },
 
 
 
 
 
 
 
 
7668
  "node_modules/wrap-ansi": {
7669
  "version": "8.1.0",
7670
  "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
 
9
  "version": "0.0.0",
10
  "dependencies": {
11
  "@aitube/clap": "^0.0.6",
12
+ "@aitube/client": "^0.0.4",
13
  "@radix-ui/react-accordion": "^1.1.2",
14
  "@radix-ui/react-avatar": "^1.0.4",
15
  "@radix-ui/react-checkbox": "^1.0.4",
 
39
  "eslint": "8.56.0",
40
  "eslint-config-next": "14.1.0",
41
  "jimp": "^0.22.12",
42
+ "jose": "^5.2.4",
43
  "lucide-react": "^0.334.0",
44
  "next": "^14.2.3",
45
  "next-themes": "^0.2.1",
 
61
  "zustand": "^4.5.2"
62
  }
63
  },
 
 
 
 
 
 
 
 
64
  "node_modules/@aitube/clap": {
65
  "version": "0.0.6",
66
  "resolved": "https://registry.npmjs.org/@aitube/clap/-/clap-0.0.6.tgz",
 
74
  }
75
  },
76
  "node_modules/@aitube/client": {
77
+ "version": "0.0.4",
78
+ "resolved": "https://registry.npmjs.org/@aitube/client/-/client-0.0.4.tgz",
79
+ "integrity": "sha512-LzOYgwSKJVslGbTEP3DEsgh5/EWupv04HNoTt3hxDQBo9/I5AF/ZwoLBDQR3QTu1i3spMJ61Sl0gVtrXxp2q3A==",
80
  "dependencies": {
81
  "uuid": "^9.0.1",
82
  "yaml": "^2.4.1"
 
5308
  "jiti": "bin/jiti.js"
5309
  }
5310
  },
5311
+ "node_modules/jose": {
5312
+ "version": "5.2.4",
5313
+ "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.4.tgz",
5314
+ "integrity": "sha512-6ScbIk2WWCeXkmzF6bRPmEuaqy1m8SbsRFMa/FLrSCkGIhj8OLVG/IH+XHVmNMx/KUo8cVWEE6oKR4dJ+S0Rkg==",
5315
+ "funding": {
5316
+ "url": "https://github.com/sponsors/panva"
5317
+ }
5318
+ },
5319
  "node_modules/jpeg-js": {
5320
  "version": "0.4.4",
5321
  "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
 
5864
  }
5865
  },
5866
  "node_modules/optionator": {
5867
+ "version": "0.9.4",
5868
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
5869
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
5870
  "dependencies": {
 
5871
  "deep-is": "^0.1.3",
5872
  "fast-levenshtein": "^2.0.6",
5873
  "levn": "^0.4.1",
5874
  "prelude-ls": "^1.2.1",
5875
+ "type-check": "^0.4.0",
5876
+ "word-wrap": "^1.2.5"
5877
  },
5878
  "engines": {
5879
  "node": ">= 0.8.0"
 
7666
  "url": "https://github.com/sponsors/ljharb"
7667
  }
7668
  },
7669
+ "node_modules/word-wrap": {
7670
+ "version": "1.2.5",
7671
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
7672
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
7673
+ "engines": {
7674
+ "node": ">=0.10.0"
7675
+ }
7676
+ },
7677
  "node_modules/wrap-ansi": {
7678
  "version": "8.1.0",
7679
  "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
package.json CHANGED
@@ -10,7 +10,7 @@
10
  },
11
  "dependencies": {
12
  "@aitube/clap": "^0.0.6",
13
- "@aitube/client": "^0.0.3",
14
  "@radix-ui/react-accordion": "^1.1.2",
15
  "@radix-ui/react-avatar": "^1.0.4",
16
  "@radix-ui/react-checkbox": "^1.0.4",
@@ -40,6 +40,7 @@
40
  "eslint": "8.56.0",
41
  "eslint-config-next": "14.1.0",
42
  "jimp": "^0.22.12",
 
43
  "lucide-react": "^0.334.0",
44
  "next": "^14.2.3",
45
  "next-themes": "^0.2.1",
 
10
  },
11
  "dependencies": {
12
  "@aitube/clap": "^0.0.6",
13
+ "@aitube/client": "^0.0.4",
14
  "@radix-ui/react-accordion": "^1.1.2",
15
  "@radix-ui/react-avatar": "^1.0.4",
16
  "@radix-ui/react-checkbox": "^1.0.4",
 
40
  "eslint": "8.56.0",
41
  "eslint-config-next": "14.1.0",
42
  "jimp": "^0.22.12",
43
+ "jose": "^5.2.4",
44
  "lucide-react": "^0.334.0",
45
  "next": "^14.2.3",
46
  "next-themes": "^0.2.1",
src/app/main.tsx CHANGED
@@ -12,9 +12,9 @@ import { useStore } from './store'
12
  import { TextareaField } from '@/components/form/textarea-field'
13
  import { DeviceFrameset } from 'react-device-frameset'
14
  import 'react-device-frameset/styles/marvel-devices.min.css'
15
- import { generateClap } from './server/aitube/generateClap'
16
- import { extendClap } from './server/aitube/extendClap'
17
- import { exportClap } from './server/aitube/exportClap'
18
 
19
  export function Main() {
20
  const [_isPending, startTransition] = useTransition()
@@ -53,7 +53,7 @@ export function Main() {
53
 
54
  let clap: ClapProject | undefined = undefined
55
  try {
56
- clap = await generateClap({ prompt })
57
 
58
  console.log(`handleSubmit(): received a clap = `, clap)
59
  setStoryGenerationStatus("finished")
@@ -68,7 +68,7 @@ export function Main() {
68
 
69
  try {
70
  setImageGenerationStatus("generating")
71
- clap = await extendClap({ clap })
72
 
73
  console.log(`handleSubmit(): received a clap with images = `, clap)
74
  setImageGenerationStatus("finished")
@@ -84,7 +84,7 @@ export function Main() {
84
  let assetUrl = ""
85
  try {
86
  setVideoGenerationStatus("generating")
87
- assetUrl = await exportClap({ clap })
88
 
89
  console.log(`handleSubmit(): received a video:`, assetUrl)
90
  setVideoGenerationStatus("finished")
 
12
  import { TextareaField } from '@/components/form/textarea-field'
13
  import { DeviceFrameset } from 'react-device-frameset'
14
  import 'react-device-frameset/styles/marvel-devices.min.css'
15
+ import { createClap } from './server/aitube/createClap'
16
+ import { editClapStoryboards } from './server/aitube/editClapStoryboards'
17
+ import { exportClapToVideo } from './server/aitube/exportClapToVideo'
18
 
19
  export function Main() {
20
  const [_isPending, startTransition] = useTransition()
 
53
 
54
  let clap: ClapProject | undefined = undefined
55
  try {
56
+ clap = await createClap({ prompt })
57
 
58
  console.log(`handleSubmit(): received a clap = `, clap)
59
  setStoryGenerationStatus("finished")
 
68
 
69
  try {
70
  setImageGenerationStatus("generating")
71
+ clap = await editClapStoryboards({ clap })
72
 
73
  console.log(`handleSubmit(): received a clap with images = `, clap)
74
  setImageGenerationStatus("finished")
 
84
  let assetUrl = ""
85
  try {
86
  setVideoGenerationStatus("generating")
87
+ assetUrl = await exportClapToVideo({ clap })
88
 
89
  console.log(`handleSubmit(): received a video:`, assetUrl)
90
  setVideoGenerationStatus("finished")
src/app/server/aitube/config.ts CHANGED
@@ -1,5 +1,2 @@
1
 
2
  export const serverHuggingfaceApiKey = `${process.env.HF_API_TOKEN || ""}`
3
-
4
- export const aitubeUrl = `${process.env.AI_TUBE_URL || "" }`
5
- export const aitubeApiUrl = aitubeUrl + (aitubeUrl.endsWith("/") ? "" : "/") + "api/"
 
1
 
2
  export const serverHuggingfaceApiKey = `${process.env.HF_API_TOKEN || ""}`
 
 
 
src/app/server/aitube/{generateClap.ts → createClap.ts} RENAMED
@@ -1,12 +1,19 @@
1
  "use server"
2
 
3
  import { ClapProject } from "@aitube/clap"
4
- import * as aitube from "@aitube/client"
5
 
6
- export async function generateClap({
 
 
7
  prompt = "",
8
  }: {
9
  prompt: string
10
  }): Promise<ClapProject> {
11
- return aitube.generateClap({ prompt })
12
- }
 
 
 
 
 
 
1
  "use server"
2
 
3
  import { ClapProject } from "@aitube/clap"
4
+ import { createClap as apiCreateClap } from "@aitube/client"
5
 
6
+ import { getToken } from "./getToken"
7
+
8
+ export async function createClap({
9
  prompt = "",
10
  }: {
11
  prompt: string
12
  }): Promise<ClapProject> {
13
+ const clap: ClapProject = await apiCreateClap({
14
+ prompt,
15
+ token: await getToken()
16
+ })
17
+
18
+ return clap
19
+ }
src/app/server/aitube/editClapStoryboards.ts ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use server"
2
+
3
+ import { ClapProject } from "@aitube/clap"
4
+ import { editClapStoryboards as apiEditClapStoryboards } from "@aitube/client"
5
+
6
+ import { getToken } from "./getToken"
7
+
8
+ export async function editClapStoryboards({
9
+ clap,
10
+ }: {
11
+ clap: ClapProject
12
+ }): Promise<ClapProject> {
13
+ const newClap: ClapProject = await apiEditClapStoryboards({
14
+ clap,
15
+ token: await getToken()
16
+ })
17
+
18
+ return newClap
19
+ }
src/app/server/aitube/exportClap.ts DELETED
@@ -1,12 +0,0 @@
1
- "use server"
2
-
3
- import { ClapProject } from "@aitube/clap"
4
- import * as aitube from "@aitube/client"
5
-
6
- export async function exportClap({
7
- clap,
8
- }: {
9
- clap: ClapProject
10
- }): Promise<string> {
11
- return aitube.exportClap({ clap })
12
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app/server/aitube/exportClapToVideo.ts ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use server"
2
+
3
+ import { ClapProject } from "@aitube/clap"
4
+ import { exportClapToVideo as apiExportClapToVideo } from "@aitube/client"
5
+
6
+ import { getToken } from "./getToken"
7
+
8
+ export async function exportClapToVideo({
9
+ clap,
10
+ }: {
11
+ clap: ClapProject
12
+ }): Promise<string> {
13
+ // TODO: maybe we should return a blob instead,
14
+ // as this could be big eg. a few megabytes
15
+ // or maybe we should convert it to an object id
16
+ const dataUri: string = await apiExportClapToVideo({
17
+ clap,
18
+ format: "mp4",
19
+ token: await getToken()
20
+ })
21
+
22
+ return dataUri
23
+ }
src/app/server/aitube/extendClap.ts DELETED
@@ -1,12 +0,0 @@
1
- "use server"
2
-
3
- import { ClapProject } from "@aitube/clap"
4
- import * as aitube from "@aitube/client"
5
-
6
- export async function extendClap({
7
- clap,
8
- }: {
9
- clap: ClapProject
10
- }): Promise<ClapProject> {
11
- return aitube.extendClap({ clap })
12
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app/server/aitube/getToken.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createSecretKey } from "crypto"
2
+ import { SignJWT } from "jose"
3
+
4
+ // https://jmswrnr.com/blog/protecting-next-js-api-routes-query-parameters
5
+
6
+ export async function getToken(data: Record<string, any> = {}): Promise<string> {
7
+ const secretKey = createSecretKey(`${process.env.AI_TUBE_API_SECRET_JWT_KEY || ""}`, 'utf-8');
8
+
9
+ const jwtToken = await new SignJWT(data)
10
+ .setProtectedHeader({
11
+ alg: 'HS256'
12
+ }) // algorithm
13
+ .setIssuedAt()
14
+ .setIssuer(`${process.env.AI_TUBE_API_SECRET_JWT_ISSUER || ""}`) // issuer
15
+ .setAudience(`${process.env.AI_TUBE_API_SECRET_JWT_AUDIENCE || ""}`) // audience
16
+ .setExpirationTime("1 day") // token expiration time - to prevent hackers from re-using our URLs more than a day
17
+ .sign(secretKey); // secretKey generated from previous step
18
+
19
+ return jwtToken
20
+ }