jbilcke-hf HF staff commited on
Commit
a899dc1
1 Parent(s): 85d2530

let's use the AiTube API

Browse files
package-lock.json CHANGED
@@ -8,6 +8,8 @@
8
  "name": "@jbilcke-hf/ai-stories-factory",
9
  "version": "0.0.0",
10
  "dependencies": {
 
 
11
  "@radix-ui/react-accordion": "^1.1.2",
12
  "@radix-ui/react-avatar": "^1.0.4",
13
  "@radix-ui/react-checkbox": "^1.0.4",
@@ -66,6 +68,31 @@
66
  "node": ">=0.10.0"
67
  }
68
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  "node_modules/@alloc/quick-lru": {
70
  "version": "5.2.0",
71
  "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
 
8
  "name": "@jbilcke-hf/ai-stories-factory",
9
  "version": "0.0.0",
10
  "dependencies": {
11
+ "@aitube/clap": "^0.0.3",
12
+ "@aitube/client": "^0.0.0",
13
  "@radix-ui/react-accordion": "^1.1.2",
14
  "@radix-ui/react-avatar": "^1.0.4",
15
  "@radix-ui/react-checkbox": "^1.0.4",
 
68
  "node": ">=0.10.0"
69
  }
70
  },
71
+ "node_modules/@aitube/clap": {
72
+ "version": "0.0.3",
73
+ "resolved": "https://registry.npmjs.org/@aitube/clap/-/clap-0.0.3.tgz",
74
+ "integrity": "sha512-lmCUP3ElK+/WFU00ieRVHMSpiTy6X0I1wDngETJ4NZQIso+KVvlVsLrSfuJZj5cIB6fafpN7UGYDcyE956uS7w==",
75
+ "dependencies": {
76
+ "uuid": "^9.0.1",
77
+ "yaml": "^2.4.1"
78
+ },
79
+ "peerDependencies": {
80
+ "typescript": "^5.4.5"
81
+ }
82
+ },
83
+ "node_modules/@aitube/client": {
84
+ "version": "0.0.0",
85
+ "resolved": "https://registry.npmjs.org/@aitube/client/-/client-0.0.0.tgz",
86
+ "integrity": "sha512-xaHZ2SWUD04MHdLv1P1V4i8AVceld6ECyw9X3KoFvOX0H2/qDBySD6NMB/9GrCa+ASebEsQ8WGzXZGQIgdeCVQ==",
87
+ "dependencies": {
88
+ "@aitube/clap": "^0.0.3",
89
+ "uuid": "^9.0.1",
90
+ "yaml": "^2.4.1"
91
+ },
92
+ "peerDependencies": {
93
+ "typescript": "^5.4.5"
94
+ }
95
+ },
96
  "node_modules/@alloc/quick-lru": {
97
  "version": "5.2.0",
98
  "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
package.json CHANGED
@@ -9,6 +9,8 @@
9
  "lint": "next lint"
10
  },
11
  "dependencies": {
 
 
12
  "@radix-ui/react-accordion": "^1.1.2",
13
  "@radix-ui/react-avatar": "^1.0.4",
14
  "@radix-ui/react-checkbox": "^1.0.4",
 
9
  "lint": "next lint"
10
  },
11
  "dependencies": {
12
+ "@aitube/clap": "^0.0.3",
13
+ "@aitube/client": "^0.0.0",
14
  "@radix-ui/react-accordion": "^1.1.2",
15
  "@radix-ui/react-avatar": "^1.0.4",
16
  "@radix-ui/react-checkbox": "^1.0.4",
src/app/main.tsx CHANGED
@@ -1,6 +1,7 @@
1
  "use client"
2
 
3
  import React, { useEffect, useRef, useState, useTransition } from 'react'
 
4
 
5
  import { Card, CardContent, CardHeader } from '@/components/ui/card'
6
  import { Button } from '@/components/ui/button'
@@ -12,7 +13,6 @@ import { TextareaField } from '@/components/form/textarea-field'
12
  import { DeviceFrameset } from 'react-device-frameset'
13
  import 'react-device-frameset/styles/marvel-devices.min.css'
14
  import { generateClap } from './server/aitube/generateClap'
15
- import { ClapOutputType, ClapProject } from '@/lib/clap/types'
16
  import { extendClap } from './server/aitube/extendClap'
17
  import { exportClap } from './server/aitube/exportClap'
18
 
 
1
  "use client"
2
 
3
  import React, { useEffect, useRef, useState, useTransition } from 'react'
4
+ import { ClapProject } from '@aitube/clap'
5
 
6
  import { Card, CardContent, CardHeader } from '@/components/ui/card'
7
  import { Button } from '@/components/ui/button'
 
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
 
src/app/server/{config.ts → aitube/config.ts} RENAMED
@@ -3,8 +3,3 @@ 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/"
6
-
7
- console.log("debug:", {
8
- aitubeUrl,
9
- aitubeApiUrl,
10
- })
 
3
 
4
  export const aitubeUrl = `${process.env.AI_TUBE_URL || "" }`
5
  export const aitubeApiUrl = aitubeUrl + (aitubeUrl.endsWith("/") ? "" : "/") + "api/"
 
 
 
 
 
src/app/server/aitube/exportClap.ts CHANGED
@@ -1,35 +1,12 @@
1
  "use server"
2
 
3
- import { ClapProject } from "@/lib/clap/types"
4
- import { serializeClap } from "@/lib/clap/serializeClap"
5
- import { blobToDataUri } from "@/lib/base64/blobToDataUri"
6
-
7
- import { aitubeApiUrl } from "../config"
8
 
9
  export async function exportClap({
10
  clap,
11
  }: {
12
  clap: ClapProject
13
  }): Promise<string> {
14
- if (!clap) { throw new Error(`please provide a clap`) }
15
-
16
- // remember: a space needs to be public for the classic fetch() to work
17
- const res = await fetch(`${aitubeApiUrl}generate/video`, {
18
- method: "POST",
19
- headers: {
20
- "Content-Type": "application/json",
21
- // TODO pass the JWT so that only the AI Stories Factory can call the API
22
- // Authorization: `Bearer ${hfApiToken}`,
23
- },
24
- body: await serializeClap(clap),
25
- cache: "no-store",
26
- // we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
27
- // next: { revalidate: 1 }
28
- })
29
-
30
- const blob = await res.blob()
31
-
32
- const dataURL = await blobToDataUri(blob)
33
-
34
- return dataURL
35
  }
 
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/extendClap.ts CHANGED
@@ -1,35 +1,12 @@
1
  "use server"
2
 
3
- import { parseClap } from "@/lib/clap/parseClap"
4
- import { ClapProject } from "@/lib/clap/types"
5
-
6
- import { aitubeApiUrl } from "../config"
7
- import { serializeClap } from "@/lib/clap/serializeClap"
8
 
9
  export async function extendClap({
10
  clap,
11
  }: {
12
  clap: ClapProject
13
  }): Promise<ClapProject> {
14
- if (!clap) { throw new Error(`please provide a prompt`) }
15
-
16
- // remember: a space needs to be public for the classic fetch() to work
17
- const res = await fetch(`${aitubeApiUrl}generate/storyboards`, {
18
- method: "POST",
19
- headers: {
20
- "Content-Type": "application/json",
21
- // TODO pass the JWT so that only the AI Stories Factory can call the API
22
- // Authorization: `Bearer ${hfApiToken}`,
23
- },
24
- body: await serializeClap(clap),
25
- cache: "no-store",
26
- // we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
27
- // next: { revalidate: 1 }
28
- })
29
-
30
- const blob = await res.blob()
31
-
32
- const extendedClap = await parseClap(blob)
33
-
34
- return extendedClap
35
  }
 
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/generateClap.ts CHANGED
@@ -1,44 +1,12 @@
1
  "use server"
2
 
3
- import { parseClap } from "@/lib/clap/parseClap"
4
- import { ClapProject } from "@/lib/clap/types"
5
-
6
- import { aitubeApiUrl } from "../config"
7
 
8
  export async function generateClap({
9
  prompt = "",
10
  }: {
11
  prompt: string
12
  }): Promise<ClapProject> {
13
- if (!prompt) { throw new Error(`please provide a prompt`) }
14
-
15
- // AiTube Stories is nice, but we also need to leave some compute for AiTube Live and AiTube Gaming
16
- const height = 1024
17
- const width = 512
18
-
19
- // console.log(`calling `+ gradioApi + (gradioApi.endsWith("/") ? "" : "/") + "api/predict")
20
-
21
- // remember: a space needs to be public for the classic fetch() to work
22
- const res = await fetch(`${aitubeApiUrl}generate/story`, {
23
- method: "POST",
24
- headers: {
25
- "Content-Type": "application/json",
26
- // TODO pass the JWT so that only the AI Stories Factory can call the API
27
- // Authorization: `Bearer ${hfApiToken}`,
28
- },
29
- body: JSON.stringify({
30
- prompt,
31
- width,
32
- height
33
- }),
34
- cache: "no-store",
35
- // we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
36
- // next: { revalidate: 1 }
37
- })
38
-
39
- const blob = await res.blob()
40
-
41
- const clap = await parseClap(blob)
42
-
43
- return clap
44
  }
 
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
  }
src/lib/clap/clap-specification-draft.md DELETED
@@ -1,162 +0,0 @@
1
- # CLAP Format Specification
2
-
3
- - Status: DRAFT
4
- - Document revision: 0.0.1
5
- - Last updated: Feb 6th, 2024
6
- - Author(s): Julian BILCKE (@flngr)
7
-
8
- ## BEFORE YOU READ
9
-
10
- The CLAP format spec is experimental and not finished yet!
11
- There might be inconsistencies, unnecessary redundancies or blatant omissions.
12
-
13
- ## What are CLAP files?
14
-
15
- The CLAP format (.clap) is a file format designed for AI video projects.
16
-
17
- It preserves prompts and assets into the same container, making it easier to share an AI video project between different people or applications.
18
-
19
- ## Structure
20
-
21
- A CLAP is an array of objects serialized into a YAML text string, then finally compressed using gzip to a binary file.
22
-
23
- The file extension is `.clap`
24
- The mime type is `application/x-yaml`
25
-
26
- There can be 5 different types of objects:
27
-
28
- - one HEADER
29
- - one METADATA
30
- - zero, one or more MODEL(s)
31
- - zero, one or more SCENE(s)
32
- - zero, one or more SEGMENT(s)
33
-
34
- This can be represented in javascript like this:
35
-
36
- ```javascript
37
- [
38
- clapHeader, // one metadata object
39
- clapMeta, // one metadata object
40
- ...clapModels, // optional array of models
41
- ...clapScenes, // optional array of scenes
42
- ...clapSegments // optional array of segments
43
- ]
44
- ```
45
-
46
- ## Header
47
-
48
- The HEADER provides information about how to decode a CLAP.
49
-
50
- Knowing in advance the number of models, scenes and segments helps the decoder parsing the information,
51
- and in some implementation, help with debugging, logging, and provisioning memory usage.
52
-
53
- However in the future, it is possible that a different scheme is used, in order to support streaming.
54
-
55
- Either by recognizing the shape of each object (fields), or by using a specific field eg. a `_type`.
56
-
57
- ```typescript
58
- {
59
- // used to know which format version is used.
60
- // CLAP is still in development and the format is not fully specified yet,
61
- // during the period most .clap file will have the "clap-0" format
62
- format: "clap-0"
63
-
64
- numberOfModels: number // integer
65
- numberOfScenes: number // integer
66
- numberOfSegments: number // integer
67
- }
68
- ```
69
-
70
- ## Metadata
71
-
72
- ```typescript
73
- {
74
- id: string // "<a valid UUID V4>"
75
- title: string // "project title"
76
- description: string // "project description"
77
- licence: string // "information about licensing"
78
-
79
- // this provides information about the image ratio
80
- // this might be removed in the final spec, as this
81
- // can be re-computed from width and height
82
- orientation: "landscape" | "vertical" | "square"
83
-
84
- // the expected duration of the project
85
- durationInMs: number
86
-
87
- // the suggested width and height of the video
88
- // note that this is just an indicator,
89
- // and might be superseeded by the application reading the .clap file
90
- width: number // integer between 256 and 8192 (value in pixels)
91
- height: number // integer between 256 and 8192 (value in pixels)
92
-
93
- // name of the suggested video model to use
94
- // note that this is just an indicator,
95
- // and might be superseeded by the application reading the .clap file
96
- defaultVideoModel: string
97
-
98
- // additional prompt to use in the video generation
99
- // this helps adding some magic touch and flair to the videos,
100
- // but perhaps the field should be renamed
101
- extraPositivePrompt: string
102
-
103
- // the screenplay (script) of the video
104
- screenplay: string
105
-
106
- // whether to loop the content by default or not
107
- isLoop: boolean
108
-
109
- // helper to indicate whether the .clap might contain interactive elements
110
- isInteractive: boolean
111
- }
112
- ```
113
-
114
- ## Models
115
-
116
- Before talking about models, first we should describe the concept of entity:
117
-
118
- in a story, an entity is something (person, place, vehicle, animal, robot, alien, object) with a name, a description of the appearance, an age, mileage or quality, an origin, and so on.
119
-
120
- An example could be "a giant magical school bus, with appearance of a cat with wheels, and which talks"
121
-
122
- The CLAP model would be an instance (an interpretation) of this entity, where we would assign it an identity:
123
- - a name and age
124
- - a visual style (a photo of the magic school bus cat)
125
- - a voice style
126
- - and maybe other things eg. an origin or background story
127
-
128
- As you can see, it can be difficult to create clearly separated categories, like "vehicule", "character", or "location"
129
- (the magical cat bus could turn into a location in some scene, a speaking character in another etc)
130
-
131
- This is why there is a common schema for all models:
132
-
133
- ```typescript
134
- {
135
- id: string
136
- category: ClapSegmentCategory
137
- triggerName: string
138
- label: string
139
- description: string
140
- author: string
141
- thumbnailUrl: string
142
- seed: number
143
-
144
- assetSourceType: ClapAssetSource
145
- assetUrl: string
146
-
147
- age: number
148
- gender: ClapModelGender
149
- region: ClapModelRegion
150
- appearance: ClapModelAppearance
151
- voiceVendor: ClapVoiceVendor
152
- voiceId: string
153
- }
154
- ```
155
-
156
- ## Atomic types
157
-
158
- ...
159
-
160
- ## TO BE CONTINUED
161
-
162
- (you can read "./types.ts" for more information)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/clap/clapToDataUri.ts DELETED
@@ -1,11 +0,0 @@
1
-
2
- import { blobToDataUri } from "@/lib/base64/blobToDataUri"
3
-
4
- import { serializeClap } from "./serializeClap"
5
- import { ClapProject } from "./types"
6
-
7
- export async function clapToDataUri(clap: ClapProject): Promise<string> {
8
- const archive = await serializeClap(clap)
9
- const dataUri = await blobToDataUri(archive, "application/x-gzip")
10
- return dataUri
11
- }
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/clap/emptyClap.ts DELETED
@@ -1,18 +0,0 @@
1
- import { newClap } from "./newClap"
2
- import { serializeClap } from "./serializeClap"
3
-
4
- let globalState: {
5
- blob?: Blob
6
- } = {
7
- blob: undefined
8
- }
9
-
10
- export async function getEmptyClap(): Promise<Blob> {
11
- if (globalState.blob) { return globalState.blob }
12
-
13
- const clap = newClap()
14
-
15
- globalState.blob = await serializeClap(clap)
16
-
17
- return globalState.blob
18
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/clap/generateClapFromSimpleStory.ts DELETED
@@ -1,149 +0,0 @@
1
- import { newClap } from "./newClap"
2
- import { newSegment } from "./newSegment"
3
- import { ClapProject } from "./types"
4
-
5
- let defaultSegmentDurationInMs = 2500 // 2584
6
-
7
- const fishDemoStory = [
8
- "Siamese fighting fish, bokeh, underwater, coral, lively, bubbles, translucency, perfect",
9
-
10
- // this one is magnificient!
11
- "princess parrot fish, bokeh, underwater, coral, lively, bubbles, translucency, perfect",
12
-
13
- "pacific ocean perch, bokeh, underwater, coral, lively, bubbles, translucency, perfect",
14
-
15
- "Queen angelfish, bokeh, underwater, coral, lively, bubbles, translucency, perfect",
16
-
17
- "sea turtle, bokeh, underwater, coral, lively, bubbles, translucency, perfect",
18
-
19
- "hippocampus, bokeh, underwater, coral, lively, bubbles, translucency, perfect",
20
- ]
21
-
22
- let demoStory = [
23
- ...fishDemoStory,
24
-
25
- // "portrait of one man news anchor, 60yo, thin, fit, american, mustache, beard, wearing a suit, medium-shot, central park, outside, serious, bokeh, perfect",
26
-
27
- // "screenshot from Call of Duty, FPS game, nextgen, videogame screenshot, unreal engine, raytracing, perfect",
28
-
29
- // "screenshot from a flight simulator, nextgen, videogame screenshot, unreal engine, raytracing, perfect",
30
- // "screenshot from fallout3, fallout4, western, wasteland, 3rd person RPG, nextgen, videogame screenshot, unreal engine, raytracing, perfect",
31
- // "portrait of single influencer woman, 30yo, thin, fit, american, wearing a red tshirt, medium-shot, central park, outside, serious, bokeh, perfect",
32
- ]
33
-
34
-
35
- export function generateClapFromSimpleStory({
36
- story = demoStory,
37
- showIntroPoweredByEngine = false,
38
- showIntroDisclaimerAboutAI = false,
39
- }: {
40
- story?: string[]
41
- showIntroPoweredByEngine?: boolean
42
- showIntroDisclaimerAboutAI?: boolean
43
- } = {
44
- story: demoStory,
45
- showIntroPoweredByEngine: false,
46
- showIntroDisclaimerAboutAI: false,
47
- }): ClapProject {
48
-
49
- const clap = newClap({
50
- meta: {
51
- title: "Interactive Demo",
52
- isInteractive: true,
53
- isLoop: true,
54
- }
55
- })
56
-
57
- let currentElapsedTimeInMs = 0
58
- let currentSegmentDurationInMs = defaultSegmentDurationInMs
59
-
60
- if (showIntroPoweredByEngine) {
61
- clap.segments.push(newSegment({
62
- startTimeInMs: currentElapsedTimeInMs,
63
- endTimeInMs: currentSegmentDurationInMs,
64
- category: "interface",
65
- prompt: "<BUILTIN:POWERED_BY_ENGINE>",
66
- label: "disclaimer",
67
- outputType: "interface",
68
- }))
69
- currentElapsedTimeInMs += currentSegmentDurationInMs
70
- }
71
-
72
- if (showIntroDisclaimerAboutAI) {
73
- clap.segments.push(newSegment({
74
- startTimeInMs: currentElapsedTimeInMs,
75
- endTimeInMs: currentSegmentDurationInMs,
76
- category: "interface",
77
- prompt: "<BUILTIN:DISCLAIMER_ABOUT_AI>",
78
- label: "disclaimer",
79
- outputType: "interface",
80
- }))
81
- currentElapsedTimeInMs += currentSegmentDurationInMs
82
- }
83
-
84
- /*
85
- clap.segments.push(
86
- newSegment({
87
- // id: string
88
- // track: number
89
- startTimeInMs: currentElapsedTimeInMs,
90
- endTimeInMs: currentSegmentDurationInMs,
91
- category: "interface",
92
- // modelId: string
93
- // sceneId: string
94
- prompt: "a hello world",
95
- label: "hello world",
96
- outputType: "interface"
97
- // renderId: string
98
- // status: ClapSegmentStatus
99
- // assetUrl: string
100
- // assetDurationInMs: number
101
- // createdBy: ClapAuthor
102
- // editedBy: ClapAuthor
103
- // outputGain: number
104
- // seed: number
105
- })
106
- )
107
-
108
- currentElapsedTimeInMs += currentSegmentDurationInMs
109
- */
110
-
111
-
112
-
113
- for (let prompt of story) {
114
-
115
- clap.segments.push(newSegment({
116
- track: 0,
117
- startTimeInMs: currentElapsedTimeInMs,
118
- endTimeInMs: currentSegmentDurationInMs,
119
- category: "video",
120
- prompt: "",
121
- label: "video",
122
- outputType: "video",
123
- }))
124
- clap.segments.push(newSegment({
125
- track: 1,
126
- startTimeInMs: currentElapsedTimeInMs,
127
- endTimeInMs: currentSegmentDurationInMs,
128
- category: "generic",
129
- prompt,
130
- label: prompt,
131
- outputType: "text",
132
- }))
133
- clap.segments.push(newSegment({
134
- track: 2,
135
- startTimeInMs: currentElapsedTimeInMs,
136
- endTimeInMs: currentSegmentDurationInMs,
137
- category: "camera",
138
- prompt: "medium-shot",
139
- label: "medium-shot",
140
- outputType: "text",
141
- }))
142
-
143
- currentElapsedTimeInMs += currentSegmentDurationInMs
144
- }
145
-
146
- clap.meta.durationInMs = currentElapsedTimeInMs
147
-
148
- return clap
149
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/clap/getClapAssetSource.ts DELETED
@@ -1,25 +0,0 @@
1
- import { ClapAssetSource } from "./types"
2
-
3
- export function getClapAssetSourceType(input: string = ""): ClapAssetSource {
4
-
5
- const str = `${input || ""}`.trim()
6
-
7
- if (!str || !str.length) {
8
- return "EMPTY"
9
- }
10
-
11
- if (str.startsWith("https://") || str.startsWith("http://")) {
12
- return "REMOTE"
13
- }
14
-
15
- // note that "path" assets are potentially a security risk, they need to be treated with care
16
- if (str.startsWith("/") || str.startsWith("../") || str.startsWith("./")) {
17
- return "PATH"
18
- }
19
-
20
- if (str.startsWith("data:")) {
21
- return "DATA"
22
- }
23
-
24
- return "PROMPT"
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/clap/newClap.ts DELETED
@@ -1,37 +0,0 @@
1
-
2
- import { v4 as uuidv4 } from "uuid"
3
-
4
- import { ClapMeta, ClapModel, ClapProject, ClapScene, ClapSegment } from "./types"
5
- import { getValidNumber } from "@/lib/utils/getValidNumber"
6
-
7
- // generate an empty clap file, or copy one from a source
8
- export function newClap(clap: {
9
- meta?: Partial<ClapMeta>
10
- models?: ClapModel[]
11
- scenes?: ClapScene[]
12
- segments?: ClapSegment[]
13
- } = {}): ClapProject {
14
-
15
- const meta: ClapMeta = {
16
- id: clap?.meta?.id === "string" ? clap.meta.id : uuidv4(),
17
- title: clap?.meta?.title === "string" ? clap.meta.title : "",
18
- description: typeof clap?.meta?.description === "string" ? clap.meta.description : "",
19
- synopsis: typeof clap?.meta?.synopsis === "string" ? clap.meta.synopsis : "",
20
- licence: typeof clap?.meta?.licence === "string" ? clap.meta.licence : "",
21
- orientation: clap?.meta?.orientation === "portrait" ? "portrait" : clap?.meta?.orientation === "square" ? "square" : "landscape",
22
- durationInMs: getValidNumber(clap?.meta?.durationInMs, 1000, Number.MAX_SAFE_INTEGER, 4000),
23
- width: getValidNumber(clap?.meta?.width, 256, 8192, 1024),
24
- height: getValidNumber(clap?.meta?.height, 256, 8192, 576),
25
- defaultVideoModel: typeof clap?.meta?.defaultVideoModel === "string" ? clap.meta.defaultVideoModel : "SVD",
26
- extraPositivePrompt: Array.isArray(clap?.meta?.extraPositivePrompt) ? clap.meta.extraPositivePrompt : [],
27
- screenplay: typeof clap?.meta?.screenplay === "string" ? clap.meta.screenplay : "",
28
- isLoop: typeof clap?.meta?.isLoop === "boolean" ? clap.meta.isLoop : false,
29
- isInteractive: typeof clap?.meta?.isInteractive === "boolean" ? clap.meta.isInteractive : false,
30
- }
31
-
32
- const models: ClapModel[] = clap?.models && Array.isArray(clap.models) ? clap.models : []
33
- const scenes: ClapScene[] = clap?.scenes && Array.isArray(clap.scenes) ? clap.scenes : []
34
- const segments: ClapSegment[] = clap?.segments && Array.isArray(clap.segments) ? clap.segments : []
35
-
36
- return { meta, models, scenes, segments }
37
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/clap/newSegment.ts DELETED
@@ -1,46 +0,0 @@
1
- import { v4 as uuidv4 } from "uuid"
2
-
3
- import { generateSeed } from "../utils/generateSeed"
4
- import { ClapSegment } from "./types"
5
- import { isValidNumber } from "../utils/isValidNumber"
6
-
7
- export function newSegment(maybeSegment?: Partial<ClapSegment>) {
8
-
9
- const startTimeInMs =
10
- isValidNumber(maybeSegment?.startTimeInMs)
11
- ? (maybeSegment?.startTimeInMs || 0)
12
- : 0
13
-
14
- const assetDurationInMs =
15
- isValidNumber(maybeSegment?.assetDurationInMs)
16
- ? (maybeSegment?.assetDurationInMs || 0)
17
- : 1000
18
-
19
- const endTimeInMs =
20
- isValidNumber(maybeSegment?.endTimeInMs)
21
- ? (maybeSegment?.endTimeInMs || 0)
22
- : (startTimeInMs + assetDurationInMs)
23
-
24
- const segment: ClapSegment = {
25
- id: typeof maybeSegment?.id === "string" ? maybeSegment.id : uuidv4(),
26
- track: isValidNumber(maybeSegment?.track) ? (maybeSegment?.track || 0) : 0,
27
- startTimeInMs,
28
- endTimeInMs,
29
- category: typeof maybeSegment?.category === "string" ? maybeSegment.category : "generic",
30
- modelId: typeof maybeSegment?.modelId === "string" ? maybeSegment.modelId : "",
31
- sceneId: typeof maybeSegment?.sceneId === "string" ? maybeSegment.sceneId : "",
32
- prompt: typeof maybeSegment?.prompt === "string" ? maybeSegment.prompt : "",
33
- label: typeof maybeSegment?.label === "string" ? maybeSegment.label : "",
34
- outputType: typeof maybeSegment?.outputType === "string" ? maybeSegment.outputType : "text",
35
- renderId: typeof maybeSegment?.renderId === "string" ? maybeSegment.renderId : "",
36
- status: typeof maybeSegment?.status === "string" ? maybeSegment.status : "to_generate",
37
- assetUrl: typeof maybeSegment?.assetUrl === "string" ? maybeSegment.assetUrl : "",
38
- assetDurationInMs,
39
- createdBy: typeof maybeSegment?.createdBy === "string" ? maybeSegment.createdBy : "ai",
40
- editedBy: typeof maybeSegment?.editedBy === "string" ? maybeSegment.editedBy : "ai",
41
- outputGain: isValidNumber(maybeSegment?.outputGain) ? (maybeSegment?.outputGain || 0) : 0,
42
- seed: isValidNumber(maybeSegment?.seed) ? (maybeSegment?.seed || 0) : generateSeed()
43
- }
44
-
45
- return segment
46
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/clap/parseClap.ts DELETED
@@ -1,319 +0,0 @@
1
- import YAML from "yaml"
2
- import { v4 as uuidv4 } from "uuid"
3
-
4
- import { ClapHeader, ClapMeta, ClapModel, ClapProject, ClapScene, ClapSegment } from "./types"
5
- import { dataUriToBlob } from "@/lib/base64/dataUriToBlob"
6
- import { getValidNumber } from "@/lib/utils/getValidNumber"
7
-
8
- type StringOrBlob = string | Blob
9
-
10
- /**
11
- * Import a clap file from various data sources into an ClapProject
12
- *
13
- * Inputs can be:
14
- * - a Clap project (which is an object)
15
- * - an URL to a remote .clap file
16
- * - a string containing a YAML array
17
- * - a data uri containing a gzipped YAML array
18
- * - a Blob containing a gzipped YAML array
19
- *
20
- * note: it is not really async, because for some reason YAML.parse is a blocking call like for JSON,
21
- * there is no async version although we are now in the 20s not 90s
22
- */
23
- export async function parseClap(src?: ClapProject | string | Blob, debug = false): Promise<ClapProject> {
24
-
25
- try {
26
- if (
27
- typeof src === "object" &&
28
- Array.isArray( (src as any)?.scenes) &&
29
- Array.isArray((src as any)?.models)
30
- ) {
31
- if (debug) {
32
- console.log("parseClap: input is already a Clap file, nothing to do:", src)
33
- }
34
- // we can skip verification
35
- return src as ClapProject
36
- }
37
- } catch (err) {
38
- // well, this is not a clap project
39
- }
40
-
41
- let stringOrBlob = (src || "") as StringOrBlob
42
-
43
- // both should work
44
- const dataUriHeader1 = "data:application/x-gzip;base64,"
45
- const dataUriHeader2 = "data:application/octet-stream;base64,"
46
-
47
- const inputIsString = typeof stringOrBlob === "string"
48
- const inputIsDataUri = typeof stringOrBlob === "string" ? stringOrBlob.startsWith(dataUriHeader1) || stringOrBlob.startsWith(dataUriHeader2) : false
49
- const inputIsRemoteFile = typeof stringOrBlob === "string" ? (stringOrBlob.startsWith("http://") || stringOrBlob.startsWith("https://")) : false
50
-
51
- let inputIsBlob = typeof stringOrBlob !== "string"
52
-
53
- let inputYamlArrayString = ""
54
-
55
- if (debug) {
56
- console.log(`parseClap: pre-analysis: ${JSON.stringify({
57
- inputIsString,
58
- inputIsBlob,
59
- inputIsDataUri,
60
- inputIsRemoteFile
61
- }, null, 2)}`)
62
- }
63
-
64
- if (typeof stringOrBlob === "string") {
65
- if (debug) {
66
- console.log("parseClap: input is a string ", stringOrBlob.slice(0, 120))
67
- }
68
- if (inputIsDataUri) {
69
- if (debug) {
70
- console.log(`parseClap: input is a data uri archive`)
71
- }
72
- stringOrBlob = dataUriToBlob(stringOrBlob, "application/x-gzip")
73
- if (debug) {
74
- console.log(`parseClap: inputBlob = `, stringOrBlob)
75
- }
76
- inputIsBlob = true
77
- } else if (inputIsRemoteFile) {
78
- try {
79
- if (debug) {
80
- console.log(`parseClap: input is a remote .clap file`)
81
- }
82
- const res = await fetch(stringOrBlob)
83
- stringOrBlob = await res.blob()
84
- if (!stringOrBlob) { throw new Error("blob is empty") }
85
- inputIsBlob = true
86
- } catch (err) {
87
- // url seems invalid
88
- throw new Error(`failed to download the .clap file (${err})`)
89
- }
90
- } else {
91
- if (debug) {
92
- console.log("parseClap: input is a text string containing a YAML array")
93
- }
94
- inputYamlArrayString = stringOrBlob
95
- inputIsBlob = false
96
- }
97
- }
98
-
99
- if (typeof stringOrBlob !== "string" && stringOrBlob) {
100
- if (debug) {
101
- console.log("parseClap: decompressing the blob..")
102
- }
103
- // Decompress the input blob using gzip
104
- const decompressedStream = stringOrBlob.stream().pipeThrough(new DecompressionStream('gzip'))
105
-
106
- try {
107
- // Convert the stream to text using a Response object
108
- const decompressedOutput = new Response(decompressedStream)
109
- // decompressedOutput.headers.set("Content-Type", "application/x-gzip")
110
- if (debug) {
111
- console.log("parseClap: decompressedOutput: ", decompressedOutput)
112
- }
113
- // const blobAgain = await decompressedOutput.blob()
114
- inputYamlArrayString = await decompressedOutput.text()
115
-
116
- if (debug && inputYamlArrayString) {
117
- console.log("parseClap: successfully decompressed the blob!")
118
- }
119
- } catch (err) {
120
- const message = `parseClap: failed to decompress (${err})`
121
- console.error(message)
122
- throw new Error(message)
123
- }
124
- }
125
-
126
- // we don't need this anymore I think
127
- // new Blob([inputStringOrBlob], { type: "application/x-yaml" })
128
-
129
- let maybeArray: any = {}
130
- try {
131
- if (debug) {
132
- console.log("parseClap: parsing the YAML array..")
133
- }
134
- // Parse YAML string to raw data
135
- maybeArray = YAML.parse(inputYamlArrayString)
136
- } catch (err) {
137
- throw new Error("invalid clap file (input string is not YAML)")
138
- }
139
-
140
- if (!Array.isArray(maybeArray) || maybeArray.length < 2) {
141
- throw new Error("invalid clap file (need a clap format header block and project metadata block)")
142
- }
143
-
144
- if (debug) {
145
- console.log("parseClap: the YAML seems okay, continuing decoding..")
146
- }
147
-
148
- const maybeClapHeader = maybeArray[0] as ClapHeader
149
-
150
- if (maybeClapHeader.format !== "clap-0") {
151
- throw new Error("invalid clap file (sorry, but you can't make up version numbers like that)")
152
- }
153
-
154
-
155
- const maybeClapMeta = maybeArray[1] as ClapMeta
156
-
157
- const clapMeta: ClapMeta = {
158
- id: typeof maybeClapMeta.title === "string" ? maybeClapMeta.id : uuidv4(),
159
- title: typeof maybeClapMeta.title === "string" ? maybeClapMeta.title : "",
160
- description: typeof maybeClapMeta.description === "string" ? maybeClapMeta.description : "",
161
- synopsis: typeof maybeClapMeta.synopsis === "string" ? maybeClapMeta.synopsis : "",
162
- licence: typeof maybeClapMeta.licence === "string" ? maybeClapMeta.licence : "",
163
- orientation: maybeClapMeta.orientation === "portrait" ? "portrait" : maybeClapMeta.orientation === "square" ? "square" : "landscape",
164
- durationInMs: getValidNumber(maybeClapMeta.durationInMs, 1000, Number.MAX_SAFE_INTEGER, 4000),
165
- width: getValidNumber(maybeClapMeta.width, 128, 8192, 1024),
166
- height: getValidNumber(maybeClapMeta.height, 128, 8192, 576),
167
- defaultVideoModel: typeof maybeClapMeta.defaultVideoModel === "string" ? maybeClapMeta.defaultVideoModel : "SVD",
168
- extraPositivePrompt: Array.isArray(maybeClapMeta.extraPositivePrompt) ? maybeClapMeta.extraPositivePrompt : [],
169
- screenplay: typeof maybeClapMeta.screenplay === "string" ? maybeClapMeta.screenplay : "",
170
- isLoop: typeof maybeClapMeta.isLoop === "boolean" ? maybeClapMeta.isLoop : false,
171
- isInteractive: typeof maybeClapMeta.isInteractive === "boolean" ? maybeClapMeta.isInteractive : false,
172
- }
173
-
174
- /*
175
- in case we want to support streaming (mix of models and segments etc), we could do it this way:
176
-
177
- const maybeModelsOrSegments = rawData.slice(2)
178
- maybeModelsOrSegments.forEach((unknownElement: any) => {
179
- if (isValidNumber(unknownElement?.track)) {
180
- maybeSegments.push(unknownElement as ClapSegment)
181
- } else {
182
- maybeModels.push(unknownElement as ClapModel)
183
- }
184
- })
185
- */
186
-
187
-
188
- const expectedNumberOfModels = maybeClapHeader.numberOfModels || 0
189
- const expectedNumberOfScenes = maybeClapHeader.numberOfScenes || 0
190
- const expectedNumberOfSegments = maybeClapHeader.numberOfSegments || 0
191
-
192
- // note: we assume the order is strictly enforced!
193
- // if you implement streaming (mix of models and segments) you will have to rewrite this!
194
-
195
- const afterTheHeaders = 2
196
- const afterTheModels = afterTheHeaders + expectedNumberOfModels
197
-
198
- const afterTheScenes = afterTheModels + expectedNumberOfScenes
199
-
200
- // note: if there are no expected models, maybeModels will be empty
201
- const maybeModels = maybeArray.slice(afterTheHeaders, afterTheModels) as ClapModel[]
202
-
203
- // note: if there are no expected scenes, maybeScenes will be empty
204
- const maybeScenes = maybeArray.slice(afterTheModels, afterTheScenes) as ClapScene[]
205
-
206
- const maybeSegments = maybeArray.slice(afterTheScenes) as ClapSegment[]
207
-
208
- const clapModels: ClapModel[] = maybeModels.map(({
209
- id,
210
- category,
211
- triggerName,
212
- label,
213
- description,
214
- author,
215
- thumbnailUrl,
216
- seed,
217
- assetSourceType,
218
- assetUrl,
219
- age,
220
- gender,
221
- region,
222
- appearance,
223
- voiceVendor,
224
- voiceId,
225
- }) => ({
226
- // TODO: we should verify each of those, probably
227
- id,
228
- category,
229
- triggerName,
230
- label,
231
- description,
232
- author,
233
- thumbnailUrl,
234
- seed,
235
- assetSourceType,
236
- assetUrl,
237
- age,
238
- gender,
239
- region,
240
- appearance,
241
- voiceVendor,
242
- voiceId,
243
- }))
244
-
245
- const clapScenes: ClapScene[] = maybeScenes.map(({
246
- id,
247
- scene,
248
- line,
249
- rawLine,
250
- sequenceFullText,
251
- sequenceStartAtLine,
252
- sequenceEndAtLine,
253
- startAtLine,
254
- endAtLine,
255
- events,
256
- }) => ({
257
- id,
258
- scene,
259
- line,
260
- rawLine,
261
- sequenceFullText,
262
- sequenceStartAtLine,
263
- sequenceEndAtLine,
264
- startAtLine,
265
- endAtLine,
266
- events: events.map(e => e)
267
- }))
268
-
269
- const clapSegments: ClapSegment[] = maybeSegments.map(({
270
- id,
271
- track,
272
- startTimeInMs,
273
- endTimeInMs,
274
- category,
275
- modelId,
276
- sceneId,
277
- prompt,
278
- label,
279
- outputType,
280
- renderId,
281
- status,
282
- assetUrl,
283
- assetDurationInMs,
284
- createdBy,
285
- editedBy,
286
- outputGain,
287
- seed,
288
- }) => ({
289
- // TODO: we should verify each of those, probably
290
- id,
291
- track,
292
- startTimeInMs,
293
- endTimeInMs,
294
- category,
295
- modelId,
296
- sceneId,
297
- prompt,
298
- label,
299
- outputType,
300
- renderId,
301
- status,
302
- assetUrl,
303
- assetDurationInMs,
304
- createdBy,
305
- editedBy,
306
- outputGain,
307
- seed,
308
- }))
309
-
310
- if (debug) {
311
- console.log(`parseClap: successfully parsed ${clapModels.length} models, ${clapScenes.length} scenes and ${clapSegments.length} segments`)
312
- }
313
- return {
314
- meta: clapMeta,
315
- models: clapModels,
316
- scenes: clapScenes,
317
- segments: clapSegments
318
- }
319
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/clap/serializeClap.ts DELETED
@@ -1,169 +0,0 @@
1
- import YAML from "yaml"
2
- import { v4 as uuidv4 } from "uuid"
3
-
4
- import { getValidNumber } from "@/lib/utils/getValidNumber"
5
-
6
- import { ClapHeader, ClapMeta, ClapModel, ClapProject, ClapScene, ClapSegment } from "./types"
7
-
8
- export async function serializeClap({
9
- meta, // ClapMeta
10
- models, // ClapModel[]
11
- scenes, // ClapScene[]
12
- segments, // ClapSegment[]
13
- }: ClapProject): Promise<Blob> {
14
-
15
- // we play it safe, and we verify the structure of the parameters,
16
- // to make sure we generate a valid clap file
17
- const clapModels: ClapModel[] = models.map(({
18
- id,
19
- category,
20
- triggerName,
21
- label,
22
- description,
23
- author,
24
- thumbnailUrl,
25
- seed,
26
- assetSourceType,
27
- assetUrl,
28
- age,
29
- gender,
30
- region,
31
- appearance,
32
- voiceVendor,
33
- voiceId,
34
- }) => ({
35
- id,
36
- category,
37
- triggerName,
38
- label,
39
- description,
40
- author,
41
- thumbnailUrl,
42
- seed,
43
- assetSourceType,
44
- assetUrl,
45
- age,
46
- gender,
47
- region,
48
- appearance,
49
- voiceVendor,
50
- voiceId,
51
- }))
52
-
53
- const clapScenes: ClapScene[] = scenes.map(({
54
- id,
55
- scene,
56
- line,
57
- rawLine,
58
- sequenceFullText,
59
- sequenceStartAtLine,
60
- sequenceEndAtLine,
61
- startAtLine,
62
- endAtLine,
63
- events,
64
- }) => ({
65
- id,
66
- scene,
67
- line,
68
- rawLine,
69
- sequenceFullText,
70
- sequenceStartAtLine,
71
- sequenceEndAtLine,
72
- startAtLine,
73
- endAtLine,
74
- events: events.map(e => e)
75
- }))
76
-
77
- const clapSegments: ClapSegment[] = segments.map(({
78
- id,
79
- track,
80
- startTimeInMs,
81
- endTimeInMs,
82
- category,
83
- modelId,
84
- sceneId,
85
- prompt,
86
- label,
87
- outputType,
88
- renderId,
89
- status,
90
- assetUrl,
91
- assetDurationInMs,
92
- createdBy,
93
- editedBy,
94
- outputGain,
95
- seed,
96
- }) => ({
97
- id,
98
- track,
99
- startTimeInMs,
100
- endTimeInMs,
101
- category,
102
- modelId,
103
- sceneId,
104
- prompt,
105
- label,
106
- outputType,
107
- renderId,
108
- status,
109
- assetUrl,
110
- assetDurationInMs,
111
- createdBy,
112
- editedBy,
113
- outputGain,
114
- seed,
115
- }))
116
-
117
- const clapHeader: ClapHeader = {
118
- format: "clap-0",
119
- numberOfModels: clapModels.length,
120
- numberOfScenes: clapScenes.length,
121
- numberOfSegments: clapSegments.length,
122
- }
123
-
124
- const clapMeta: ClapMeta = {
125
- id: meta.id || uuidv4(),
126
- title: typeof meta.title === "string" ? meta.title : "Untitled",
127
- description: typeof meta.description === "string" ? meta.description : "",
128
- synopsis: typeof meta.synopsis === "string" ? meta.synopsis : "",
129
- licence: typeof meta.licence === "string" ? meta.licence : "",
130
- orientation: meta.orientation === "portrait" ? "portrait" : meta.orientation === "square" ? "square" : "landscape",
131
- durationInMs: getValidNumber(meta.durationInMs, 1000, Number.MAX_SAFE_INTEGER, 4000),
132
- width: getValidNumber(meta.width, 256, 8192, 1024),
133
- height: getValidNumber(meta.height, 256, 8192, 576),
134
- defaultVideoModel: typeof meta.defaultVideoModel === "string" ? meta.defaultVideoModel : "SVD",
135
- extraPositivePrompt: Array.isArray(meta.extraPositivePrompt) ? meta.extraPositivePrompt : [],
136
- screenplay: typeof meta.screenplay === "string" ? meta.screenplay : "",
137
- isLoop: typeof meta.screenplay === "boolean" ? meta.screenplay : false,
138
- isInteractive: typeof meta.isInteractive === "boolean" ? meta.isInteractive : false,
139
- }
140
-
141
- const entries = [
142
- clapHeader,
143
- clapMeta,
144
- ...clapModels,
145
- ...clapScenes,
146
- ...clapSegments
147
- ]
148
-
149
- const strigifiedResult = YAML.stringify(entries)
150
-
151
- // Convert the string to a Blob
152
- const blobResult = new Blob([strigifiedResult], { type: "application/x-yaml" })
153
-
154
- // Create a stream for the blob
155
- const readableStream = blobResult.stream()
156
-
157
- // Compress the stream using gzip
158
- const compressionStream = new CompressionStream('gzip')
159
- const compressedStream = readableStream.pipeThrough(compressionStream)
160
-
161
- // Create a new blob from the compressed stream
162
- const response = new Response(compressedStream)
163
-
164
- response.headers.set("Content-Type", "application/x-gzip")
165
-
166
- const compressedBlob = await response.blob()
167
-
168
- return compressedBlob
169
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/clap/types.ts DELETED
@@ -1,203 +0,0 @@
1
-
2
- export type ClapSegmentCategory =
3
- | "splat"
4
- | "mesh"
5
- | "depth"
6
- | "event"
7
- | "interface"
8
- | "phenomenon"
9
- | "video"
10
- | "storyboard"
11
- | "transition"
12
- | "characters"
13
- | "location"
14
- | "time"
15
- | "era"
16
- | "lighting"
17
- | "weather"
18
- | "action"
19
- | "music"
20
- | "sound"
21
- | "dialogue"
22
- | "style"
23
- | "camera"
24
- | "generic"
25
-
26
- export type ClapOutputType =
27
- | "text"
28
- | "animation"
29
- | "interface"
30
- | "event"
31
- | "phenomenon"
32
- | "transition"
33
- | "image"
34
- | "video"
35
- | "audio"
36
-
37
- export type ClapSegmentStatus =
38
- | "to_generate"
39
- | "to_interpolate"
40
- | "to_upscale"
41
- | "completed"
42
- | "error"
43
-
44
- export type ClapAuthor =
45
- | "auto" // the element was edited automatically using basic if/else logical rules
46
- | "ai" // the element was edited using a large language model
47
- | "human" // the element was edited by a human
48
-
49
- export type ClapAssetSource =
50
- | "REMOTE" // http:// or https://
51
-
52
- // note that "path" assets are potentially a security risk, they need to be treated with care
53
- | "PATH" // a file path eg. /path or ./path/to/ or ../path/to/
54
-
55
- | "DATA" // a data URI, starting with data:
56
-
57
- | "PROMPT" // by default, a plain text prompt
58
-
59
- | "EMPTY"
60
-
61
- export type ClapModelGender =
62
- | "male"
63
- | "female"
64
- | "person"
65
- | "object"
66
-
67
- export type ClapModelAppearance = "serious" | "neutral" | "friendly" | "chill"
68
-
69
- // this is used for accent, style..
70
- export type ClapModelRegion =
71
- | "american"
72
- | "british"
73
- | "australian"
74
- | "canadian"
75
- | "indian"
76
- | "french"
77
- | "italian"
78
- | "german"
79
- | "chinese"
80
-
81
- // note: this is all very subjective, so please use good judgment
82
- //
83
- // "deep" might indicate a deeper voice tone, thicker, rich in harmonics
84
- // in this context, it is used to indicate voices that could
85
- // be associated with African American (AADOS) characters
86
- //
87
- // "high" could be used for some other countries, eg. asia
88
- export type ClapModelTimbre = "high" | "neutral" | "deep"
89
-
90
- export type ClapVoiceVendor = "ElevenLabs" | "XTTS"
91
-
92
- export type ClapVoice = {
93
- name: string
94
- gender: ClapModelGender
95
- age: number
96
- region: ClapModelRegion
97
- timbre: ClapModelTimbre
98
- appearance: ClapModelAppearance
99
- voiceVendor: ClapVoiceVendor
100
- voiceId: string
101
- }
102
-
103
- export type ClapHeader = {
104
- format: "clap-0"
105
- numberOfModels: number
106
- numberOfScenes: number
107
- numberOfSegments: number
108
- }
109
-
110
- export type ClapMeta = {
111
- id: string
112
- title: string
113
- description: string
114
- synopsis: string
115
- licence: string
116
- orientation: string
117
-
118
- // the default duration of the experience
119
- // the real one might last longer if made interactive
120
- durationInMs: number
121
-
122
- width: number
123
- height: number
124
- defaultVideoModel: string
125
- extraPositivePrompt: string[]
126
- screenplay: string
127
- isLoop: boolean
128
- isInteractive: boolean
129
- }
130
-
131
- export type ClapSceneEvent = {
132
- id: string
133
- type: "description" | "dialogue" | "action"
134
- character?: string
135
- description: string
136
- behavior: string
137
- startAtLine: number
138
- endAtLine: number
139
- }
140
-
141
- export type ClapScene = {
142
- id: string
143
- scene: string
144
- line: string
145
- rawLine: string
146
- sequenceFullText: string
147
- sequenceStartAtLine: number
148
- sequenceEndAtLine: number
149
- startAtLine: number
150
- endAtLine: number
151
- events: ClapSceneEvent[]
152
- }
153
-
154
- export type ClapSegment = {
155
- id: string
156
- track: number // usually track 0 is the video, track 1 is the storyboard, track 2 is the camera
157
- startTimeInMs: number
158
- endTimeInMs: number
159
- category: ClapSegmentCategory
160
- modelId: string
161
- sceneId: string
162
- prompt: string
163
- label: string
164
- outputType: ClapOutputType
165
- renderId: string
166
- status: ClapSegmentStatus
167
- assetUrl: string
168
- assetDurationInMs: number
169
- createdBy: ClapAuthor
170
- editedBy: ClapAuthor
171
- outputGain: number
172
- seed: number
173
- }
174
-
175
- export type ClapModel = {
176
- id: string
177
- category: ClapSegmentCategory
178
- triggerName: string
179
- label: string
180
- description: string
181
- author: string
182
- thumbnailUrl: string
183
- seed: number
184
-
185
- assetSourceType: ClapAssetSource
186
- assetUrl: string
187
-
188
- // those are only used by certain types of models
189
- age: number
190
- gender: ClapModelGender
191
- region: ClapModelRegion
192
- appearance: ClapModelAppearance
193
- voiceVendor: ClapVoiceVendor
194
- voiceId: string
195
- }
196
-
197
- export type ClapProject = {
198
- meta: ClapMeta
199
- models: ClapModel[]
200
- scenes: ClapScene[]
201
- segments: ClapSegment[]
202
- // let's keep room for other stuff (screenplay etc)
203
- }