jbilcke-hf HF staff commited on
Commit
58cb97c
β€’
1 Parent(s): fd46ec1

work in progress

Browse files
Files changed (41) hide show
  1. package-lock.json +0 -0
  2. package.json +3 -3
  3. src/app/main.tsx +31 -18
  4. src/components/editors/FilterEditor/FilterTree/index.tsx +74 -0
  5. src/components/editors/FilterEditor/FilterTree/useFilterTree.ts +147 -0
  6. src/components/editors/FilterEditor/FilterViewer/index.tsx +33 -0
  7. src/components/editors/FilterEditor/index.tsx +17 -0
  8. src/components/editors/ProjectEditor/index.tsx +0 -1
  9. src/components/editors/SegmentEditor/index.tsx +0 -1
  10. src/components/editors/WorkflowEditor/WorkflowViewer/ReactFlowCanvas/formats/comfyui/types.ts +12 -12
  11. src/components/editors/WorkflowEditor/WorkflowViewer/ReactFlowCanvas/samples/comfyicu.ts +394 -0
  12. src/components/editors/WorkflowEditor/WorkflowViewer/index.tsx +1 -1
  13. src/experiments/grading/types.ts +0 -7
  14. src/lib/core/constants.ts +1 -1
  15. src/services/debug.ts +2 -0
  16. src/services/editors/entity-editor/getDefaultEntityEditorState.ts +2 -1
  17. src/{experiments/grading β†’ services/editors/filter-editor}/README.md +0 -0
  18. src/{experiments/grading/applyColorGradingDemo.ts β†’ services/editors/filter-editor/demo.ts} +2 -2
  19. src/{experiments/grading β†’ services/editors/filter-editor}/filters/DEPRECATED_analogFilm.ts +4 -3
  20. src/{experiments/grading β†’ services/editors/filter-editor}/filters/analogLens.ts +4 -3
  21. src/{experiments/grading β†’ services/editors/filter-editor}/filters/cinematic.ts +4 -3
  22. src/{experiments/grading β†’ services/editors/filter-editor}/filters/colorMapping.ts +4 -3
  23. src/{experiments/grading β†’ services/editors/filter-editor}/filters/colorTemperature.ts +4 -3
  24. src/{experiments/grading β†’ services/editors/filter-editor}/filters/crossProcessing.ts +4 -3
  25. src/{experiments/grading β†’ services/editors/filter-editor}/filters/filmDegradation.ts +4 -3
  26. src/services/editors/filter-editor/filters/index.ts +137 -0
  27. src/{experiments/grading β†’ services/editors/filter-editor}/filters/infrared.ts +4 -3
  28. src/{experiments/grading β†’ services/editors/filter-editor}/filters/lomography.ts +4 -3
  29. src/{experiments/grading β†’ services/editors/filter-editor}/filters/splitToning.ts +4 -3
  30. src/{experiments/grading β†’ services/editors/filter-editor}/filters/toneMapping.ts +4 -3
  31. src/{experiments/grading β†’ services/editors/filter-editor}/filters/vintageFilm.ts +4 -3
  32. src/services/editors/filter-editor/getDefaultFilterEditorState.ts +29 -0
  33. src/{experiments/grading/applyColorGrading.ts β†’ services/editors/filter-editor/runFilterPipeline.ts} +9 -8
  34. src/services/editors/filter-editor/useFilterEditor.ts +52 -0
  35. src/services/editors/index.ts +1 -0
  36. src/services/editors/project-editor/getDefaultProjectEditorState.ts +2 -1
  37. src/services/editors/script-editor/getDefaultScriptEditorState.ts +2 -1
  38. src/services/editors/segment-editor/getDefaultSegmentEditorState.ts +2 -1
  39. src/services/editors/workflow-editor/getDefaultWorkflowtEditorState.ts +2 -1
  40. src/services/index.ts +1 -0
  41. src/services/plugins/usePlugins.ts +2 -0
package-lock.json CHANGED
The diff for this file is too large to render. See raw diff
 
package.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "clapper",
3
- "version": "0.0.6",
4
  "private": true,
5
  "description": "🎬 Clapper",
6
  "license": "GPL-3.0-only",
@@ -38,9 +38,9 @@
38
  "dependencies": {
39
  "@aitube/broadway": "0.1.2",
40
  "@aitube/clap": "0.1.2",
41
- "@aitube/clapper-services": "0.1.2-8",
42
  "@aitube/engine": "0.1.2",
43
- "@aitube/timeline": "0.1.2-2",
44
  "@fal-ai/serverless-client": "^0.13.0",
45
  "@ffmpeg/ffmpeg": "^0.12.10",
46
  "@ffmpeg/util": "^0.12.1",
 
1
  {
2
  "name": "clapper",
3
+ "version": "0.0.7",
4
  "private": true,
5
  "description": "🎬 Clapper",
6
  "license": "GPL-3.0-only",
 
38
  "dependencies": {
39
  "@aitube/broadway": "0.1.2",
40
  "@aitube/clap": "0.1.2",
41
+ "@aitube/clapper-services": "0.1.2-14",
42
  "@aitube/engine": "0.1.2",
43
+ "@aitube/timeline": "0.1.2-3",
44
  "@fal-ai/serverless-client": "^0.13.0",
45
  "@ffmpeg/ffmpeg": "^0.12.10",
46
  "@ffmpeg/util": "^0.12.1",
src/app/main.tsx CHANGED
@@ -26,6 +26,7 @@ import { ScriptEditor } from '@/components/editors/ScriptEditor'
26
  import { SegmentEditor } from '@/components/editors/SegmentEditor'
27
  import { EntityEditor } from '@/components/editors/EntityEditor'
28
  import { WorkflowEditor } from '@/components/editors/WorkflowEditor'
 
29
 
30
  type DroppableThing = { files: File[] }
31
 
@@ -117,7 +118,7 @@ function MainContent() {
117
  <FruityDesktop>
118
  <FruityWindow
119
  id="ScriptEditor"
120
- title="Script editor"
121
  defaultWidth={375}
122
  minWidth={200}
123
  defaultHeight={370}
@@ -131,7 +132,7 @@ function MainContent() {
131
 
132
  <FruityWindow
133
  id="SegmentEditor"
134
- title="segment editor"
135
  defaultWidth={342}
136
  minWidth={200}
137
  defaultHeight={395}
@@ -145,7 +146,7 @@ function MainContent() {
145
 
146
  <FruityWindow
147
  id="EntityEditor"
148
- title="Entity editor"
149
  defaultWidth={544}
150
  minWidth={200}
151
  defaultHeight={318}
@@ -157,21 +158,33 @@ function MainContent() {
157
  <EntityEditor />
158
  </FruityWindow>
159
 
160
- {hasBetaAccess && (
161
- <FruityWindow
162
- id="WorkflowEditor"
163
- title="Workflow editor"
164
- defaultWidth={459}
165
- minWidth={200}
166
- defaultHeight={351}
167
- minHeight={100}
168
- defaultX={536}
169
- defaultY={3}
170
- canBeClosed={false}
171
- >
172
- <WorkflowEditor />
173
- </FruityWindow>
174
- )}
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
  <FruityWindow
177
  id="Monitor"
 
26
  import { SegmentEditor } from '@/components/editors/SegmentEditor'
27
  import { EntityEditor } from '@/components/editors/EntityEditor'
28
  import { WorkflowEditor } from '@/components/editors/WorkflowEditor'
29
+ import { FilterEditor } from '@/components/editors/FilterEditor'
30
 
31
  type DroppableThing = { files: File[] }
32
 
 
118
  <FruityDesktop>
119
  <FruityWindow
120
  id="ScriptEditor"
121
+ title="Screenplay"
122
  defaultWidth={375}
123
  minWidth={200}
124
  defaultHeight={370}
 
132
 
133
  <FruityWindow
134
  id="SegmentEditor"
135
+ title="Segments"
136
  defaultWidth={342}
137
  minWidth={200}
138
  defaultHeight={395}
 
146
 
147
  <FruityWindow
148
  id="EntityEditor"
149
+ title="Entities"
150
  defaultWidth={544}
151
  minWidth={200}
152
  defaultHeight={318}
 
158
  <EntityEditor />
159
  </FruityWindow>
160
 
161
+ <FruityWindow
162
+ id="WorkflowEditor"
163
+ title="Workflows"
164
+ defaultWidth={459}
165
+ minWidth={200}
166
+ defaultHeight={351}
167
+ minHeight={100}
168
+ defaultX={536}
169
+ defaultY={3}
170
+ canBeClosed={false}
171
+ >
172
+ <WorkflowEditor />
173
+ </FruityWindow>
174
+
175
+ <FruityWindow
176
+ id="FilterEditor"
177
+ title="Filters"
178
+ defaultWidth={459}
179
+ minWidth={200}
180
+ defaultHeight={351}
181
+ minHeight={100}
182
+ defaultX={936}
183
+ defaultY={400}
184
+ canBeClosed={false}
185
+ >
186
+ <FilterEditor />
187
+ </FruityWindow>
188
 
189
  <FruityWindow
190
  id="Monitor"
src/components/editors/FilterEditor/FilterTree/index.tsx ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import { useEffect } from 'react'
4
+
5
+ import { cn } from '@/lib/utils'
6
+ import { isClapEntity } from '@/components/tree-browsers/utils/isSomething'
7
+ import { TreeNodeItem, LibraryNodeType } from '@/components/tree-browsers/types'
8
+ import { Tree } from '@/components/core/tree'
9
+
10
+ import { useFilterTree } from './useFilterTree'
11
+ import { useFilterEditor } from '@/services/editors/filter-editor/useFilterEditor'
12
+
13
+ export function FilterTree({
14
+ className = '',
15
+ }: {
16
+ className?: string
17
+ } = {}) {
18
+ const libraryTreeRoot = useFilterTree((s) => s.libraryTreeRoot)
19
+ const selectTreeNode = useFilterTree((s) => s.selectTreeNode)
20
+ const selectedTreeNodeId = useFilterTree((s) => s.selectedTreeNodeId)
21
+ const setAvailableFilters = useFilterTree((s) => s.setAvailableFilters)
22
+
23
+ const availableFilters = useFilterEditor((s) => s.availableFilters)
24
+ const activeFilters = useFilterEditor((s) => s.activeFilters)
25
+ const current = useFilterEditor((s) => s.current)
26
+
27
+ useEffect(() => {
28
+ console.log('TODO: populate the filter tree')
29
+ }, [availableFilters.map((f) => f.id).join(',')])
30
+ /**
31
+ * handle click on tree node
32
+ * yes, this is where the magic happens!
33
+ *
34
+ * @param id
35
+ * @param nodeType
36
+ * @param node
37
+ * @returns
38
+ */
39
+ const handleOnChange = async (
40
+ id: string | null,
41
+ nodeType?: LibraryNodeType,
42
+ nodeItem?: TreeNodeItem
43
+ ) => {
44
+ console.log(`calling selectTreeNodeById(id)`)
45
+ selectTreeNode(id, nodeType, nodeItem)
46
+
47
+ if (!nodeType || !nodeItem) {
48
+ console.log('tree-browser: clicked on an undefined node')
49
+ return
50
+ }
51
+ if (isClapEntity(nodeType, nodeItem)) {
52
+ // ClapEntity
53
+ } else {
54
+ console.log(
55
+ `tree-browser: no action attached to ${nodeType}, so skipping`
56
+ )
57
+ return
58
+ }
59
+ console.log(`tree-browser: clicked on a ${nodeType}`, nodeItem)
60
+ }
61
+
62
+ return (
63
+ <Tree.Root<LibraryNodeType, TreeNodeItem>
64
+ value={selectedTreeNodeId}
65
+ onChange={handleOnChange}
66
+ className={cn(`not-prose h-full w-full px-2 pt-2`, className)}
67
+ label="Filters"
68
+ >
69
+ {libraryTreeRoot.map((node) => (
70
+ <Tree.Node node={node} key={node.id} />
71
+ ))}
72
+ </Tree.Root>
73
+ )
74
+ }
src/components/editors/FilterEditor/FilterTree/useFilterTree.ts ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import { create } from 'zustand'
4
+ import { ClapEntity, UUID } from '@aitube/clap'
5
+ import { Filter } from '@aitube/clapper-services'
6
+
7
+ import {
8
+ LibraryTreeNode,
9
+ TreeNodeItem,
10
+ LibraryNodeType,
11
+ } from '@/components/tree-browsers/types'
12
+ import { icons } from '@/components/icons'
13
+ import {
14
+ collectionClassName,
15
+ libraryClassName,
16
+ } from '@/components/tree-browsers/style/treeNodeStyles'
17
+
18
+ export const useFilterTree = create<{
19
+ availableFiltersLibraryTreeNodeId: string
20
+
21
+ // activeFiltersLibraryTreeNodeId: string
22
+
23
+ libraryTreeRoot: LibraryTreeNode[]
24
+ init: () => void
25
+
26
+ /**
27
+ * Load available filters into the tree
28
+ *
29
+ * @param collections
30
+ * @returns
31
+ */
32
+ setAvailableFilters: (filters: Filter[]) => void
33
+
34
+ // we support those all selection modes for convenience - please keep them!
35
+ selectedNodeItem?: TreeNodeItem
36
+ selectedNodeType?: LibraryNodeType
37
+ selectTreeNode: (
38
+ treeNodeId?: string | null,
39
+ nodeType?: LibraryNodeType,
40
+ nodeItem?: TreeNodeItem
41
+ ) => void
42
+ selectedTreeNodeId: string | null
43
+ }>((set, get) => ({
44
+ availableFiltersLibraryTreeNodeId: '',
45
+
46
+ // activeFiltersLibraryTreeNodeId: '',
47
+
48
+ libraryTreeRoot: [],
49
+ init: () => {
50
+ const availableFilterLibrary: LibraryTreeNode = {
51
+ id: UUID(),
52
+ nodeType: 'TREE_ROOT_PROJECT',
53
+ label: 'Available filters',
54
+ icon: icons.project,
55
+ className: libraryClassName,
56
+ isExpanded: true,
57
+ children: [
58
+ {
59
+ id: UUID(),
60
+ nodeType: 'DEFAULT_TREE_NODE_EMPTY',
61
+ label: 'Empty',
62
+ icon: icons.project,
63
+ className: collectionClassName,
64
+ },
65
+ ],
66
+ }
67
+
68
+ const libraryTreeRoot = [availableFilterLibrary]
69
+
70
+ set({
71
+ availableFiltersLibraryTreeNodeId: availableFilterLibrary.id,
72
+ libraryTreeRoot,
73
+ selectedNodeItem: undefined,
74
+ selectedTreeNodeId: null,
75
+ })
76
+ },
77
+
78
+ setAvailableFilters: (filters: Filter[]) => {
79
+ const availableFilterLibrary: LibraryTreeNode = {
80
+ id: UUID(),
81
+ nodeType: 'TREE_ROOT_PROJECT',
82
+ label: 'Filters',
83
+ icon: icons.project,
84
+ className: libraryClassName,
85
+ isExpanded: true,
86
+ children: [
87
+ {
88
+ id: UUID(),
89
+ nodeType: 'DEFAULT_TREE_NODE_EMPTY',
90
+ label: 'Empty',
91
+ icon: icons.project,
92
+ className: collectionClassName,
93
+ },
94
+ ],
95
+ }
96
+
97
+ const libraryTreeRoot = [availableFilterLibrary]
98
+
99
+ set({
100
+ availableFiltersLibraryTreeNodeId: availableFilterLibrary.id,
101
+ libraryTreeRoot,
102
+ selectedNodeItem: undefined,
103
+ selectedTreeNodeId: null,
104
+ })
105
+ },
106
+
107
+ /*
108
+ setCommunityCollections: (collections: CommunityEntityCollection[]) => {
109
+ // TODO: implement this
110
+
111
+ },
112
+ */
113
+
114
+ selectedNodeItem: undefined,
115
+ selectEntity: (entity?: ClapEntity) => {
116
+ if (entity) {
117
+ console.log(
118
+ 'TODO julian: change this code to search in the entity collections'
119
+ )
120
+ const selectedTreeNode = get().libraryTreeRoot.find(
121
+ (node) => node.data?.id === entity.id
122
+ )
123
+
124
+ // set({ selectedTreeNode })
125
+ set({ selectedTreeNodeId: selectedTreeNode?.id || null })
126
+ set({ selectedNodeItem: entity })
127
+ } else {
128
+ // set({ selectedTreeNode: undefined })
129
+ set({ selectedTreeNodeId: null })
130
+ set({ selectedNodeItem: undefined })
131
+ }
132
+ },
133
+
134
+ // selectedTreeNode: undefined,
135
+ selectedTreeNodeId: null,
136
+ selectTreeNode: (
137
+ treeNodeId?: string | null,
138
+ nodeType?: LibraryNodeType,
139
+ nodeItem?: TreeNodeItem
140
+ ) => {
141
+ set({ selectedTreeNodeId: treeNodeId ? treeNodeId : undefined })
142
+ set({ selectedNodeType: nodeType ? nodeType : undefined })
143
+ set({ selectedNodeItem: nodeItem ? nodeItem : undefined })
144
+ },
145
+ }))
146
+
147
+ useFilterTree.getState().init()
src/components/editors/FilterEditor/FilterViewer/index.tsx ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { FormSection } from '@/components/forms'
2
+ import { useFilterEditor, useUI } from '@/services'
3
+
4
+ export function FilterViewer() {
5
+ const current = useFilterEditor((s) => s.current)
6
+ const setCurrent = useFilterEditor((s) => s.setCurrent)
7
+ const undo = useFilterEditor((s) => s.undo)
8
+ const redo = useFilterEditor((s) => s.redo)
9
+
10
+ const hasBetaAccess = useUI((s) => s.hasBetaAccess)
11
+
12
+ if (!hasBetaAccess) {
13
+ return (
14
+ <FormSection label={'Filter Editor'} className="p-4">
15
+ Storyboard filters are WIP and not available yet.
16
+ </FormSection>
17
+ )
18
+ }
19
+
20
+ if (!current) {
21
+ return (
22
+ <FormSection label={'Filter Editor'} className="p-4">
23
+ No filter selected.
24
+ </FormSection>
25
+ )
26
+ }
27
+
28
+ return (
29
+ <FormSection label={'Filter Editor'} className="p-4">
30
+ <p>Put filter parameters</p>
31
+ </FormSection>
32
+ )
33
+ }
src/components/editors/FilterEditor/index.tsx ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ReflexContainer, ReflexElement, ReflexSplitter } from 'react-reflex'
2
+ import { FilterTree } from './FilterTree'
3
+ import { FilterViewer } from './FilterViewer'
4
+
5
+ export function FilterEditor() {
6
+ return (
7
+ <ReflexContainer orientation="vertical">
8
+ <ReflexElement minSize={70} size={200}>
9
+ <FilterTree />
10
+ </ReflexElement>
11
+ <ReflexSplitter />
12
+ <ReflexElement minSize={70}>
13
+ <FilterViewer />
14
+ </ReflexElement>
15
+ </ReflexContainer>
16
+ )
17
+ }
src/components/editors/ProjectEditor/index.tsx CHANGED
@@ -12,7 +12,6 @@ export function ProjectEditor() {
12
 
13
  const current = useProjectEditor((s) => s.current)
14
  const setCurrent = useProjectEditor((s) => s.setCurrent)
15
- const history = useProjectEditor((s) => s.history)
16
  const undo = useProjectEditor((s) => s.undo)
17
  const redo = useProjectEditor((s) => s.redo)
18
 
 
12
 
13
  const current = useProjectEditor((s) => s.current)
14
  const setCurrent = useProjectEditor((s) => s.setCurrent)
 
15
  const undo = useProjectEditor((s) => s.undo)
16
  const redo = useProjectEditor((s) => s.redo)
17
 
src/components/editors/SegmentEditor/index.tsx CHANGED
@@ -13,7 +13,6 @@ export function SegmentEditor() {
13
  )
14
  const current = useSegmentEditor((s) => s.current)
15
  const setCurrent = useSegmentEditor((s) => s.setCurrent)
16
- const history = useSegmentEditor((s) => s.history)
17
  const undo = useSegmentEditor((s) => s.undo)
18
  const redo = useSegmentEditor((s) => s.redo)
19
 
 
13
  )
14
  const current = useSegmentEditor((s) => s.current)
15
  const setCurrent = useSegmentEditor((s) => s.setCurrent)
 
16
  const undo = useSegmentEditor((s) => s.undo)
17
  const redo = useSegmentEditor((s) => s.redo)
18
 
src/components/editors/WorkflowEditor/WorkflowViewer/ReactFlowCanvas/formats/comfyui/types.ts CHANGED
@@ -2,7 +2,7 @@ export type ComfyuiWorkflow = {
2
  extra: {
3
  ds: {
4
  scale: number
5
- offset: number[]
6
  }
7
  }
8
  links: [number, number, number, number, number, string][]
@@ -18,37 +18,37 @@ export type ComfyuiWorkflowNode = {
18
  id: number
19
  pos: number[]
20
  mode: number
21
- size: number[]
22
  type: string
23
- color?: string
24
  flags: {
25
- collapsed?: boolean
26
  }
27
  order: number
28
  inputs?: ComfyuiWorkflowNodeInput[]
29
  outputs?: ComfyuiWorkflowNodeOutput[]
30
  properties: Record<string, any>
31
- widgets_values?: any[]
32
  }
33
 
34
  export type ComfyuiWorkflowNodeInput = {
35
- link?: number
36
  name: string
37
  type: string
38
- label?: string
39
  widget?: {
40
  name: string
41
- }
42
- slot_index?: number
43
  }
44
 
45
  export type ComfyuiWorkflowNodeOutput = {
46
  name: string
47
  type: string
48
- label?: string
49
- links?: number[]
50
  shape: number
51
- slot_index?: number
52
  }
53
 
54
  export type ComfyuiWorkflowGroup = {
 
2
  extra: {
3
  ds: {
4
  scale: number
5
+ offset: { '0': number; '1': number }
6
  }
7
  }
8
  links: [number, number, number, number, number, string][]
 
18
  id: number
19
  pos: number[]
20
  mode: number
21
+ size: { '0': number; '1': number }
22
  type: string
23
+ color?: string | null
24
  flags: {
25
+ collapsed?: boolean | null
26
  }
27
  order: number
28
  inputs?: ComfyuiWorkflowNodeInput[]
29
  outputs?: ComfyuiWorkflowNodeOutput[]
30
  properties: Record<string, any>
31
+ widgets_values?: any | null
32
  }
33
 
34
  export type ComfyuiWorkflowNodeInput = {
35
+ link?: number | null
36
  name: string
37
  type: string
38
+ label?: string | null
39
  widget?: {
40
  name: string
41
+ } | null
42
+ slot_index?: number | null
43
  }
44
 
45
  export type ComfyuiWorkflowNodeOutput = {
46
  name: string
47
  type: string
48
+ label?: string | null
49
+ links?: number[] | null
50
  shape: number
51
+ slot_index?: number | null
52
  }
53
 
54
  export type ComfyuiWorkflowGroup = {
src/components/editors/WorkflowEditor/WorkflowViewer/ReactFlowCanvas/samples/comfyicu.ts ADDED
@@ -0,0 +1,394 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ComfyuiWorkflow } from '../formats/comfyui/types'
2
+
3
+ // live portrait
4
+ export const comfyicu: ComfyuiWorkflow[] = [
5
+ {
6
+ extra: {
7
+ ds: {
8
+ scale: 0.8264462809917354,
9
+ offset: {
10
+ '0': 173.4048767089844,
11
+ '1': -0.9636010527610779,
12
+ },
13
+ },
14
+ },
15
+ links: [
16
+ [30, 8, 0, 19, 0, 'IMAGE'],
17
+ [32, 19, 0, 18, 0, 'IMAGE'],
18
+ [58, 1, 0, 30, 0, 'LIVEPORTRAITPIPE'],
19
+ [59, 4, 0, 30, 1, 'IMAGE'],
20
+ [60, 8, 0, 30, 2, 'IMAGE'],
21
+ [64, 18, 0, 23, 0, 'IMAGE'],
22
+ [67, 30, 1, 18, 1, 'IMAGE'],
23
+ [68, 30, 1, 19, 1, 'IMAGE'],
24
+ ],
25
+ nodes: [
26
+ {
27
+ id: 4,
28
+ pos: [138, 323],
29
+ mode: 0,
30
+ size: {
31
+ '0': 272.85791015625,
32
+ '1': 331.6089477539062,
33
+ },
34
+ type: 'LoadImage',
35
+ flags: {},
36
+ order: 0,
37
+ outputs: [
38
+ {
39
+ name: 'IMAGE',
40
+ type: 'IMAGE',
41
+ links: [59],
42
+ shape: 3,
43
+ slot_index: 0,
44
+ },
45
+ {
46
+ name: 'MASK',
47
+ type: 'MASK',
48
+ links: null,
49
+ shape: 3,
50
+ },
51
+ ],
52
+ properties: {
53
+ 'Node name for S&R': 'LoadImage',
54
+ },
55
+ widgets_values: ['monolisa.jpg', 'image'],
56
+ },
57
+ {
58
+ id: 19,
59
+ pos: [507, 675],
60
+ mode: 0,
61
+ size: {
62
+ '0': 315,
63
+ '1': 242,
64
+ },
65
+ type: 'ImageResizeKJ',
66
+ flags: {},
67
+ order: 4,
68
+ inputs: [
69
+ {
70
+ link: 30,
71
+ name: 'image',
72
+ type: 'IMAGE',
73
+ },
74
+ {
75
+ link: 68,
76
+ name: 'get_image_size',
77
+ type: 'IMAGE',
78
+ },
79
+ {
80
+ link: null,
81
+ name: 'width_input',
82
+ type: 'INT',
83
+ widget: {
84
+ name: 'width_input',
85
+ },
86
+ },
87
+ {
88
+ link: null,
89
+ name: 'height_input',
90
+ type: 'INT',
91
+ widget: {
92
+ name: 'height_input',
93
+ },
94
+ },
95
+ ],
96
+ outputs: [
97
+ {
98
+ name: 'IMAGE',
99
+ type: 'IMAGE',
100
+ links: [32],
101
+ shape: 3,
102
+ slot_index: 0,
103
+ },
104
+ {
105
+ name: 'width',
106
+ type: 'INT',
107
+ links: null,
108
+ shape: 3,
109
+ },
110
+ {
111
+ name: 'height',
112
+ type: 'INT',
113
+ links: null,
114
+ shape: 3,
115
+ },
116
+ ],
117
+ properties: {
118
+ 'Node name for S&R': 'ImageResizeKJ',
119
+ },
120
+ widgets_values: [512, 512, 'nearest-exact', false, 2, 0, 0],
121
+ },
122
+ {
123
+ id: 18,
124
+ pos: [860, 679],
125
+ mode: 0,
126
+ size: {
127
+ '0': 210,
128
+ '1': 150,
129
+ },
130
+ type: 'ImageConcatMulti',
131
+ flags: {},
132
+ order: 5,
133
+ inputs: [
134
+ {
135
+ link: 32,
136
+ name: 'image_1',
137
+ type: 'IMAGE',
138
+ },
139
+ {
140
+ link: 67,
141
+ name: 'image_2',
142
+ type: 'IMAGE',
143
+ },
144
+ ],
145
+ outputs: [
146
+ {
147
+ name: 'images',
148
+ type: 'IMAGE',
149
+ links: [64],
150
+ shape: 3,
151
+ slot_index: 0,
152
+ },
153
+ ],
154
+ properties: {
155
+ 'Node name for S&R': 'ImageConcatMulti',
156
+ },
157
+ widgets_values: [2, 'right', false, null],
158
+ },
159
+ {
160
+ id: 1,
161
+ pos: [142, 205],
162
+ mode: 0,
163
+ size: {
164
+ '0': 252,
165
+ '1': 58,
166
+ },
167
+ type: 'DownloadAndLoadLivePortraitModels',
168
+ flags: {},
169
+ order: 1,
170
+ outputs: [
171
+ {
172
+ name: 'live_portrait_pipe',
173
+ type: 'LIVEPORTRAITPIPE',
174
+ links: [58],
175
+ shape: 3,
176
+ slot_index: 0,
177
+ },
178
+ ],
179
+ properties: {
180
+ 'Node name for S&R': 'DownloadAndLoadLivePortraitModels',
181
+ },
182
+ widgets_values: ['auto'],
183
+ },
184
+ {
185
+ id: 30,
186
+ pos: [500, 249],
187
+ mode: 0,
188
+ size: {
189
+ '0': 367.7999877929688,
190
+ '1': 362,
191
+ },
192
+ type: 'LivePortraitProcess',
193
+ flags: {},
194
+ order: 3,
195
+ inputs: [
196
+ {
197
+ link: 58,
198
+ name: 'pipeline',
199
+ type: 'LIVEPORTRAITPIPE',
200
+ },
201
+ {
202
+ link: 59,
203
+ name: 'source_image',
204
+ type: 'IMAGE',
205
+ },
206
+ {
207
+ link: 60,
208
+ name: 'driving_images',
209
+ type: 'IMAGE',
210
+ },
211
+ ],
212
+ outputs: [
213
+ {
214
+ name: 'cropped_images',
215
+ type: 'IMAGE',
216
+ links: [],
217
+ shape: 3,
218
+ slot_index: 0,
219
+ },
220
+ {
221
+ name: 'full_images',
222
+ type: 'IMAGE',
223
+ links: [67, 68],
224
+ shape: 3,
225
+ slot_index: 1,
226
+ },
227
+ ],
228
+ properties: {
229
+ 'Node name for S&R': 'LivePortraitProcess',
230
+ },
231
+ widgets_values: [
232
+ 512,
233
+ 2.3,
234
+ 0,
235
+ -0.11,
236
+ true,
237
+ false,
238
+ 1,
239
+ false,
240
+ 1,
241
+ true,
242
+ true,
243
+ 'CPU',
244
+ ],
245
+ },
246
+ {
247
+ id: 8,
248
+ pos: [161, 714],
249
+ mode: 0,
250
+ size: [235.1999969482422, 491.1999969482422],
251
+ type: 'VHS_LoadVideo',
252
+ flags: {},
253
+ order: 2,
254
+ inputs: [
255
+ {
256
+ link: null,
257
+ name: 'meta_batch',
258
+ type: 'VHS_BatchManager',
259
+ },
260
+ {
261
+ link: null,
262
+ name: 'vae',
263
+ type: 'VAE',
264
+ },
265
+ ],
266
+ outputs: [
267
+ {
268
+ name: 'IMAGE',
269
+ type: 'IMAGE',
270
+ links: [30, 60],
271
+ shape: 3,
272
+ slot_index: 0,
273
+ },
274
+ {
275
+ name: 'frame_count',
276
+ type: 'INT',
277
+ links: null,
278
+ shape: 3,
279
+ },
280
+ {
281
+ name: 'audio',
282
+ type: 'VHS_AUDIO',
283
+ links: null,
284
+ shape: 3,
285
+ },
286
+ {
287
+ name: 'video_info',
288
+ type: 'VHS_VIDEOINFO',
289
+ links: null,
290
+ shape: 3,
291
+ },
292
+ ],
293
+ properties: {
294
+ 'Node name for S&R': 'VHS_LoadVideo',
295
+ },
296
+ widgets_values: {
297
+ video: 'd1.mp4',
298
+ force_rate: 0,
299
+ force_size: 'Disabled',
300
+ custom_width: 512,
301
+ videopreview: {
302
+ hidden: false,
303
+ params: {
304
+ type: 'input',
305
+ format: 'video/mp4',
306
+ filename: 'd1.mp4',
307
+ force_rate: 0,
308
+ frame_load_cap: 0,
309
+ select_every_nth: 1,
310
+ skip_first_frames: 0,
311
+ },
312
+ paused: false,
313
+ },
314
+ custom_height: 512,
315
+ frame_load_cap: 0,
316
+ select_every_nth: 1,
317
+ skip_first_frames: 0,
318
+ 'choose video to upload': 'image',
319
+ },
320
+ },
321
+ {
322
+ id: 23,
323
+ pos: [1098, 240],
324
+ mode: 0,
325
+ size: [1253.234130859375, 940.6170654296875],
326
+ type: 'VHS_VideoCombine',
327
+ flags: {},
328
+ order: 6,
329
+ inputs: [
330
+ {
331
+ link: 64,
332
+ name: 'images',
333
+ type: 'IMAGE',
334
+ },
335
+ {
336
+ link: null,
337
+ name: 'audio',
338
+ type: 'VHS_AUDIO',
339
+ },
340
+ {
341
+ link: null,
342
+ name: 'meta_batch',
343
+ type: 'VHS_BatchManager',
344
+ },
345
+ {
346
+ link: null,
347
+ name: 'vae',
348
+ type: 'VAE',
349
+ },
350
+ ],
351
+ outputs: [
352
+ {
353
+ name: 'Filenames',
354
+ type: 'VHS_FILENAMES',
355
+ links: null,
356
+ shape: 3,
357
+ },
358
+ ],
359
+ properties: {
360
+ 'Node name for S&R': 'VHS_VideoCombine',
361
+ },
362
+ widgets_values: {
363
+ crf: 19,
364
+ format: 'video/h264-mp4',
365
+ pix_fmt: 'yuv420p',
366
+ pingpong: false,
367
+ frame_rate: 24,
368
+ loop_count: 0,
369
+ save_output: true,
370
+ videopreview: {
371
+ hidden: false,
372
+ params: {
373
+ src: 'http://comfy.icu/api/v1/view/workflows/mV51t3g_DcTcIHhwBU7p1/temp/LivePortrait_00001.mp4',
374
+ type: 'temp',
375
+ format: 'video/h264-mp4',
376
+ filename:
377
+ '/workflows/mV51t3g_DcTcIHhwBU7p1/temp/LivePortrait_00001.mp4',
378
+ subfolder: '',
379
+ frame_rate: 24,
380
+ },
381
+ paused: false,
382
+ },
383
+ save_metadata: true,
384
+ filename_prefix: 'LivePortrait',
385
+ },
386
+ },
387
+ ],
388
+ config: {},
389
+ groups: [],
390
+ version: 0.4,
391
+ last_link_id: 68,
392
+ last_node_id: 31,
393
+ },
394
+ ]
src/components/editors/WorkflowEditor/WorkflowViewer/index.tsx CHANGED
@@ -16,7 +16,7 @@ export function WorkflowViewer({
16
  <ReactFlowCanvas />
17
  ) : !current ? (
18
  <FormSection label={'Workflow Editor'} className="p-4">
19
- Workflows are not implemented yet.
20
  </FormSection>
21
  ) : (
22
  <FormSection label={'Workflow Editor'} className="p-4">
 
16
  <ReactFlowCanvas />
17
  ) : !current ? (
18
  <FormSection label={'Workflow Editor'} className="p-4">
19
+ Workflows are WIP and not available yet.
20
  </FormSection>
21
  ) : (
22
  <FormSection label={'Workflow Editor'} className="p-4">
src/experiments/grading/types.ts DELETED
@@ -1,7 +0,0 @@
1
- import { ClapInputField } from '@aitube/clap/dist/types'
2
-
3
- export type ColorGradingFilter = {
4
- name: string
5
- parameters: Array<ClapInputField>
6
- shader: string // WGSL shader code for the filter
7
- }
 
 
 
 
 
 
 
 
src/lib/core/constants.ts CHANGED
@@ -3,7 +3,7 @@
3
  export const HARD_LIMIT_NB_MAX_ASSETS_TO_GENERATE_IN_PARALLEL = 32
4
 
5
  export const APP_NAME = 'Clapper.app'
6
- export const APP_REVISION = '20240729+1441'
7
 
8
  export const APP_DOMAIN = 'Clapper.app'
9
  export const APP_LINK = 'https://clapper.app'
 
3
  export const HARD_LIMIT_NB_MAX_ASSETS_TO_GENERATE_IN_PARALLEL = 32
4
 
5
  export const APP_NAME = 'Clapper.app'
6
+ export const APP_REVISION = '20240729+2245'
7
 
8
  export const APP_DOMAIN = 'Clapper.app'
9
  export const APP_LINK = 'https://clapper.app'
src/services/debug.ts CHANGED
@@ -6,6 +6,7 @@ import { useBroadcast } from './broadcast/useBroadcast'
6
  import {
7
  useEditors,
8
  useEntityEditor,
 
9
  useProjectEditor,
10
  useScriptEditor,
11
  useSegmentEditor,
@@ -31,6 +32,7 @@ if (typeof window !== 'undefined') {
31
  w.useBroadcast = useBroadcast
32
  w.useEditors = useEditors
33
  w.useEntityEditor = useEntityEditor
 
34
  w.useProjectEditor = useProjectEditor
35
  w.useScriptEditor = useScriptEditor
36
  w.useSegmentEditor = useSegmentEditor
 
6
  import {
7
  useEditors,
8
  useEntityEditor,
9
+ useFilterEditor,
10
  useProjectEditor,
11
  useScriptEditor,
12
  useSegmentEditor,
 
32
  w.useBroadcast = useBroadcast
33
  w.useEditors = useEditors
34
  w.useEntityEditor = useEntityEditor
35
+ w.useFilterEditor = useFilterEditor
36
  w.useProjectEditor = useProjectEditor
37
  w.useScriptEditor = useScriptEditor
38
  w.useSegmentEditor = useSegmentEditor
src/services/editors/entity-editor/getDefaultEntityEditorState.ts CHANGED
@@ -2,9 +2,10 @@ import { EntityEditorState } from '@aitube/clapper-services'
2
 
3
  export function getDefaultEntityEditorState(): EntityEditorState {
4
  const state: EntityEditorState = {
 
5
  current: undefined,
 
6
  version: 0,
7
- history: [],
8
 
9
  draft: undefined,
10
  showEntityList: false,
 
2
 
3
  export function getDefaultEntityEditorState(): EntityEditorState {
4
  const state: EntityEditorState = {
5
+ before: [],
6
  current: undefined,
7
+ after: [],
8
  version: 0,
 
9
 
10
  draft: undefined,
11
  showEntityList: false,
src/{experiments/grading β†’ services/editors/filter-editor}/README.md RENAMED
File without changes
src/{experiments/grading/applyColorGradingDemo.ts β†’ services/editors/filter-editor/demo.ts} RENAMED
@@ -1,4 +1,4 @@
1
- import { applyColorGrading } from './applyColorGrading'
2
 
3
  import { analogLensSimulator } from './filters/analogLens'
4
  import { cinematic } from './filters/cinematic'
@@ -12,7 +12,7 @@ import { hdrToneMapping } from './filters/toneMapping'
12
  import { vintageFilm } from './filters/vintageFilm'
13
 
14
  export async function demo() {
15
- const base64DataUriImages = await applyColorGrading({
16
  images: [
17
  {
18
  image: 'data:image/png....',
 
1
+ import { runFilterPipeline } from './runFilterPipeline'
2
 
3
  import { analogLensSimulator } from './filters/analogLens'
4
  import { cinematic } from './filters/cinematic'
 
12
  import { vintageFilm } from './filters/vintageFilm'
13
 
14
  export async function demo() {
15
+ const base64DataUriImages = await runFilterPipeline({
16
  images: [
17
  {
18
  image: 'data:image/png....',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/DEPRECATED_analogFilm.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const analogFilmSimulator: ColorGradingFilter = {
4
- name: 'Analog Film Simulator',
 
5
  parameters: [
6
  {
7
  id: 'preset',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const analogFilmSimulator: Filter = {
4
+ id: 'analog_film_simulator',
5
+ label: 'Analog Film Simulator',
6
  parameters: [
7
  {
8
  id: 'preset',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/analogLens.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const analogLensSimulator: ColorGradingFilter = {
4
- name: 'Analog Lens Simulator',
 
5
  parameters: [
6
  {
7
  id: 'chromaticAberration',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const analogLensSimulator: Filter = {
4
+ id: 'analog_lens_simulator',
5
+ label: 'Analog Lens Simulator',
6
  parameters: [
7
  {
8
  id: 'chromaticAberration',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/cinematic.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const cinematic: ColorGradingFilter = {
4
- name: 'Cinematic Color Grading',
 
5
  parameters: [
6
  {
7
  id: 'preset',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const cinematic: Filter = {
4
+ id: 'cinematic_color_grading',
5
+ label: 'Cinematic Color Grading',
6
  parameters: [
7
  {
8
  id: 'preset',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/colorMapping.ts RENAMED
@@ -1,8 +1,9 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
  // Example filter implementations
4
- export const colorMapping: ColorGradingFilter = {
5
- name: 'Color Mapping',
 
6
  parameters: [
7
  {
8
  id: 'redMultiplier',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
  // Example filter implementations
4
+ export const colorMapping: Filter = {
5
+ id: 'color_mapping',
6
+ label: 'Color Mapping',
7
  parameters: [
8
  {
9
  id: 'redMultiplier',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/colorTemperature.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const colorTemperature: ColorGradingFilter = {
4
- name: 'Color Temperature Adjustment',
 
5
  parameters: [
6
  {
7
  id: 'temperature',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const colorTemperature: Filter = {
4
+ id: 'color_temperature_adjustment',
5
+ label: 'Color Temperature Adjustment',
6
  parameters: [
7
  {
8
  id: 'temperature',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/crossProcessing.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const crossProcessing: ColorGradingFilter = {
4
- name: 'Cross-Processing',
 
5
  parameters: [
6
  {
7
  id: 'intensity',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const crossProcessing: Filter = {
4
+ id: 'cross_processing',
5
+ label: 'Cross-Processing',
6
  parameters: [
7
  {
8
  id: 'intensity',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/filmDegradation.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const filmDegradation: ColorGradingFilter = {
4
- name: 'Film Degradation',
 
5
  parameters: [
6
  {
7
  id: 'scratchesIntensity',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const filmDegradation: Filter = {
4
+ id: 'film_degradation',
5
+ label: 'Film Degradation',
6
  parameters: [
7
  {
8
  id: 'scratchesIntensity',
src/services/editors/filter-editor/filters/index.ts ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Filter, FilterWithParams } from '@aitube/clapper-services'
2
+
3
+ export { analogLensSimulator } from './analogLens'
4
+ export { cinematic } from './cinematic'
5
+ export { colorMapping } from './colorMapping'
6
+ export { colorTemperature } from './colorTemperature'
7
+ export { crossProcessing } from './crossProcessing'
8
+ export { filmDegradation } from './filmDegradation'
9
+ export { infraredBlackAndWhite } from './infrared'
10
+ export { lomography } from './lomography'
11
+ export { splitToning } from './splitToning'
12
+ export { hdrToneMapping } from './toneMapping'
13
+ export { vintageFilm } from './vintageFilm'
14
+
15
+ import { analogLensSimulator } from './analogLens'
16
+ import { cinematic } from './cinematic'
17
+ import { colorMapping } from './colorMapping'
18
+ import { colorTemperature } from './colorTemperature'
19
+ import { crossProcessing } from './crossProcessing'
20
+ import { filmDegradation } from './filmDegradation'
21
+ import { infraredBlackAndWhite } from './infrared'
22
+ import { lomography } from './lomography'
23
+ import { splitToning } from './splitToning'
24
+ import { hdrToneMapping } from './toneMapping'
25
+ import { vintageFilm } from './vintageFilm'
26
+
27
+ export const filters: Filter[] = [
28
+ analogLensSimulator,
29
+ cinematic,
30
+ colorMapping,
31
+ colorTemperature,
32
+ crossProcessing,
33
+ filmDegradation,
34
+ infraredBlackAndWhite,
35
+ lomography,
36
+ splitToning,
37
+ hdrToneMapping,
38
+ vintageFilm,
39
+ ]
40
+
41
+ export const presets: FilterWithParams[] = [
42
+ {
43
+ filter: analogLensSimulator,
44
+ parameters: {
45
+ chromaticAberration: 0.003,
46
+ vignetteStrength: 0.4,
47
+ vignetteRadius: 0.8,
48
+ distortion: 0.15,
49
+ bloomStrength: 0.2,
50
+ bloomRadius: 4,
51
+ dofFocusDistance: 0.6,
52
+ dofFocusRange: 0.1,
53
+ dofBlurStrength: 3,
54
+ },
55
+ },
56
+ {
57
+ filter: infraredBlackAndWhite,
58
+ parameters: {
59
+ contrast: 1.3,
60
+ grain: 0.25,
61
+ glow: 0.4,
62
+ },
63
+ },
64
+ {
65
+ filter: filmDegradation,
66
+ parameters: {
67
+ scratchesIntensity: 0.35,
68
+ dustIntensity: 0.25,
69
+ colorFading: 0.4,
70
+ lightLeakIntensity: 0.3,
71
+ },
72
+ },
73
+ {
74
+ filter: crossProcessing,
75
+ parameters: {
76
+ intensity: 0.6,
77
+ contrastBoost: 0.4,
78
+ colorShift: 'Cool',
79
+ },
80
+ },
81
+ {
82
+ filter: lomography,
83
+ parameters: {
84
+ saturation: 1.4,
85
+ contrast: 1.3,
86
+ vignetteIntensity: 0.6,
87
+ lightLeakIntensity: 0.4,
88
+ },
89
+ },
90
+ {
91
+ filter: cinematic,
92
+ parameters: {
93
+ preset: 'Blade Runner',
94
+ intensity: 0.6,
95
+ contrast: 1.3,
96
+ },
97
+ },
98
+ {
99
+ filter: splitToning,
100
+ parameters: {
101
+ highlightColor: 'Yellow',
102
+ shadowColor: 'Blue',
103
+ balance: 0.1,
104
+ intensity: 0.5,
105
+ },
106
+ },
107
+
108
+ {
109
+ filter: hdrToneMapping,
110
+ parameters: {
111
+ exposure: 0.5,
112
+ contrast: 1.2,
113
+ saturation: 1.1,
114
+ highlights: -0.2,
115
+ shadows: 0.3,
116
+ },
117
+ },
118
+ {
119
+ filter: colorTemperature,
120
+ parameters: {
121
+ temperature: 5500,
122
+ tint: 10,
123
+ },
124
+ },
125
+ {
126
+ filter: vintageFilm,
127
+ parameters: {
128
+ preset: 'Kodachrome 64',
129
+ intensity: 0.8,
130
+ grain: 0.3,
131
+ ageEffect: 0.2,
132
+ colorShift: 0.05,
133
+ contrast: 1.1,
134
+ saturation: 1.2,
135
+ },
136
+ },
137
+ ]
src/{experiments/grading β†’ services/editors/filter-editor}/filters/infrared.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const infraredBlackAndWhite: ColorGradingFilter = {
4
- name: 'Infrared Black and White',
 
5
  parameters: [
6
  {
7
  id: 'contrast',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const infraredBlackAndWhite: Filter = {
4
+ id: 'infrared_black_and_white',
5
+ label: 'Infrared Black and White',
6
  parameters: [
7
  {
8
  id: 'contrast',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/lomography.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const lomography: ColorGradingFilter = {
4
- name: 'Lomography',
 
5
  parameters: [
6
  {
7
  id: 'saturation',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const lomography: Filter = {
4
+ id: 'lomography',
5
+ label: 'Lomography',
6
  parameters: [
7
  {
8
  id: 'saturation',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/splitToning.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const splitToning: ColorGradingFilter = {
4
- name: 'Split Toning',
 
5
  parameters: [
6
  {
7
  id: 'highlightColor',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const splitToning: Filter = {
4
+ id: 'split_toning',
5
+ label: 'Split Toning',
6
  parameters: [
7
  {
8
  id: 'highlightColor',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/toneMapping.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const hdrToneMapping: ColorGradingFilter = {
4
- name: 'HDR Tone Mapping',
 
5
  parameters: [
6
  {
7
  id: 'exposure',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const hdrToneMapping: Filter = {
4
+ id: 'hd_tone_mapping',
5
+ label: 'HDR Tone Mapping',
6
  parameters: [
7
  {
8
  id: 'exposure',
src/{experiments/grading β†’ services/editors/filter-editor}/filters/vintageFilm.ts RENAMED
@@ -1,7 +1,8 @@
1
- import { ColorGradingFilter } from '../types'
2
 
3
- export const vintageFilm: ColorGradingFilter = {
4
- name: 'Enhanced Vintage Film Stocks',
 
5
  parameters: [
6
  {
7
  id: 'preset',
 
1
+ import { Filter } from '@aitube/clapper-services'
2
 
3
+ export const vintageFilm: Filter = {
4
+ id: 'enhanced_vintage_film_stocks',
5
+ label: 'Enhanced Vintage Film Stocks',
6
  parameters: [
7
  {
8
  id: 'preset',
src/services/editors/filter-editor/getDefaultFilterEditorState.ts ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { FilterEditorState } from '@aitube/clapper-services'
2
+
3
+ import { cinematic, filters } from './filters'
4
+
5
+ export function getDefaultFilterEditorState(): FilterEditorState {
6
+ const state: FilterEditorState = {
7
+ before: [],
8
+ current: undefined,
9
+ after: [],
10
+ version: 0,
11
+
12
+ isEnabled: true,
13
+
14
+ availableFilters: [...filters],
15
+
16
+ activeFilters: [
17
+ {
18
+ filter: cinematic,
19
+ parameters: {
20
+ preset: 'Blade Runner',
21
+ intensity: 0.6,
22
+ contrast: 1.3,
23
+ },
24
+ },
25
+ ],
26
+ }
27
+
28
+ return state
29
+ }
src/{experiments/grading/applyColorGrading.ts β†’ services/editors/filter-editor/runFilterPipeline.ts} RENAMED
@@ -1,16 +1,17 @@
1
  import { decode, encode } from 'base64-arraybuffer'
2
 
3
- import { ColorGradingFilter } from './types'
 
 
 
 
4
 
5
- export async function applyColorGrading({
6
  images,
7
  filters,
8
  }: {
9
  images: Array<{ image: string; depthMap?: string }>
10
- filters: Array<{
11
- filter: ColorGradingFilter
12
- parameters?: Record<string, string | number>
13
- }>
14
  }): Promise<string[]> {
15
  if (!navigator.gpu) {
16
  throw new Error('WebGPU is not supported in this browser.')
@@ -105,8 +106,8 @@ async function applyFilter(
105
  device: GPUDevice,
106
  inputTexture: GPUTexture,
107
  depthTexture: GPUTexture | null,
108
- filter: ColorGradingFilter,
109
- parameters?: Record<string, string | number>
110
  ): Promise<GPUTexture> {
111
  const shader = createShaderModule(device, filter.shader)
112
  const pipeline = device.createComputePipeline({
 
1
  import { decode, encode } from 'base64-arraybuffer'
2
 
3
+ import {
4
+ Filter,
5
+ FilterParams,
6
+ FilterWithParams,
7
+ } from '@aitube/clapper-services'
8
 
9
+ export async function runFilterPipeline({
10
  images,
11
  filters,
12
  }: {
13
  images: Array<{ image: string; depthMap?: string }>
14
+ filters: FilterWithParams[]
 
 
 
15
  }): Promise<string[]> {
16
  if (!navigator.gpu) {
17
  throw new Error('WebGPU is not supported in this browser.')
 
106
  device: GPUDevice,
107
  inputTexture: GPUTexture,
108
  depthTexture: GPUTexture | null,
109
+ filter: Filter,
110
+ parameters?: FilterParams
111
  ): Promise<GPUTexture> {
112
  const shader = createShaderModule(device, filter.shader)
113
  const pipeline = device.createComputePipeline({
src/services/editors/filter-editor/useFilterEditor.ts ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import { create } from 'zustand'
4
+ import {
5
+ FilterEditorStore,
6
+ FilterWithParams,
7
+ redo,
8
+ undo,
9
+ } from '@aitube/clapper-services'
10
+
11
+ import { getDefaultFilterEditorState } from './getDefaultFilterEditorState'
12
+ import { runFilterPipeline } from './runFilterPipeline'
13
+
14
+ export const useFilterEditor = create<FilterEditorStore>()((set, get) => ({
15
+ ...getDefaultFilterEditorState(),
16
+
17
+ setEnabled: (isEnabled: boolean) => {
18
+ set({ isEnabled })
19
+ },
20
+
21
+ runFilterPipeline: async (input: string) => {
22
+ const { activeFilters } = get()
23
+ const results = await runFilterPipeline({
24
+ images: [{ image: input }],
25
+ filters: activeFilters,
26
+ })
27
+
28
+ const result = results[0]
29
+
30
+ return result
31
+ },
32
+
33
+ setCurrent: (current?: FilterWithParams[]) => {
34
+ set({ current })
35
+ },
36
+
37
+ undo: () => {
38
+ set({
39
+ ...undo<FilterWithParams[]>({
40
+ ...get(),
41
+ }),
42
+ })
43
+ },
44
+
45
+ redo: () => {
46
+ set({
47
+ ...redo<FilterWithParams[]>({
48
+ ...get(),
49
+ }),
50
+ })
51
+ },
52
+ }))
src/services/editors/index.ts CHANGED
@@ -1,4 +1,5 @@
1
  export { useEntityEditor } from './entity-editor/useEntityEditor'
 
2
  export { useProjectEditor } from './project-editor/useProjectEditor'
3
  export { useSegmentEditor } from './segment-editor/useSegmentEditor'
4
  export { useScriptEditor } from './script-editor/useScriptEditor'
 
1
  export { useEntityEditor } from './entity-editor/useEntityEditor'
2
+ export { useFilterEditor } from './filter-editor/useFilterEditor'
3
  export { useProjectEditor } from './project-editor/useProjectEditor'
4
  export { useSegmentEditor } from './segment-editor/useSegmentEditor'
5
  export { useScriptEditor } from './script-editor/useScriptEditor'
src/services/editors/project-editor/getDefaultProjectEditorState.ts CHANGED
@@ -2,9 +2,10 @@ import { ProjectEditorState } from '@aitube/clapper-services'
2
 
3
  export function getDefaultProjectEditorState(): ProjectEditorState {
4
  const state: ProjectEditorState = {
 
5
  current: undefined,
 
6
  version: 0,
7
- history: [],
8
  }
9
 
10
  return state
 
2
 
3
  export function getDefaultProjectEditorState(): ProjectEditorState {
4
  const state: ProjectEditorState = {
5
+ before: [],
6
  current: undefined,
7
+ after: [],
8
  version: 0,
 
9
  }
10
 
11
  return state
src/services/editors/script-editor/getDefaultScriptEditorState.ts CHANGED
@@ -15,9 +15,10 @@ export function getDefaultScriptEditorState(): ScriptEditorState {
15
  scrollWidth: 0,
16
  scrollTopInMs: 0,
17
 
 
18
  current: undefined,
 
19
  version: 0,
20
- history: [],
21
  }
22
 
23
  return state
 
15
  scrollWidth: 0,
16
  scrollTopInMs: 0,
17
 
18
+ before: [],
19
  current: undefined,
20
+ after: [],
21
  version: 0,
 
22
  }
23
 
24
  return state
src/services/editors/segment-editor/getDefaultSegmentEditorState.ts CHANGED
@@ -2,9 +2,10 @@ import { SegmentEditorState } from '@aitube/clapper-services'
2
 
3
  export function getDefaultSegmentEditorState(): SegmentEditorState {
4
  const state: SegmentEditorState = {
 
5
  current: undefined,
 
6
  version: 0,
7
- history: [],
8
  }
9
 
10
  return state
 
2
 
3
  export function getDefaultSegmentEditorState(): SegmentEditorState {
4
  const state: SegmentEditorState = {
5
+ before: [],
6
  current: undefined,
7
+ after: [],
8
  version: 0,
 
9
  }
10
 
11
  return state
src/services/editors/workflow-editor/getDefaultWorkflowtEditorState.ts CHANGED
@@ -2,9 +2,10 @@ import { WorkflowEditorState } from '@aitube/clapper-services'
2
 
3
  export function getDefaultWorkflowEditorState(): WorkflowEditorState {
4
  const state: WorkflowEditorState = {
 
5
  current: undefined,
 
6
  version: 0,
7
- history: [],
8
  }
9
 
10
  return state
 
2
 
3
  export function getDefaultWorkflowEditorState(): WorkflowEditorState {
4
  const state: WorkflowEditorState = {
5
+ before: [],
6
  current: undefined,
7
+ after: [],
8
  version: 0,
 
9
  }
10
 
11
  return state
src/services/index.ts CHANGED
@@ -4,6 +4,7 @@ export { useAudio } from './audio/useAudio'
4
  export { useBroadcast } from './broadcast/useBroadcast'
5
  export {
6
  useEntityEditor,
 
7
  useProjectEditor,
8
  useSegmentEditor,
9
  useScriptEditor,
 
4
  export { useBroadcast } from './broadcast/useBroadcast'
5
  export {
6
  useEntityEditor,
7
+ useFilterEditor,
8
  useProjectEditor,
9
  useSegmentEditor,
10
  useScriptEditor,
src/services/plugins/usePlugins.ts CHANGED
@@ -27,6 +27,7 @@ import {
27
  useSegmentEditor,
28
  useScriptEditor,
29
  useWorkflowEditor,
 
30
  } from '../editors'
31
  import { useSimulator } from '../simulator/useSimulator'
32
  import { useIO } from '../io/useIO'
@@ -67,6 +68,7 @@ export const usePlugins = create<PluginsStore>((set, get) => ({
67
  mic: useMic,
68
  segmentEditor: useSegmentEditor,
69
  entityEditor: useEntityEditor,
 
70
  projectEditor: useProjectEditor,
71
  scriptEditor: useScriptEditor,
72
  workflowEditor: useWorkflowEditor,
 
27
  useSegmentEditor,
28
  useScriptEditor,
29
  useWorkflowEditor,
30
+ useFilterEditor,
31
  } from '../editors'
32
  import { useSimulator } from '../simulator/useSimulator'
33
  import { useIO } from '../io/useIO'
 
68
  mic: useMic,
69
  segmentEditor: useSegmentEditor,
70
  entityEditor: useEntityEditor,
71
+ filterEditor: useFilterEditor,
72
  projectEditor: useProjectEditor,
73
  scriptEditor: useScriptEditor,
74
  workflowEditor: useWorkflowEditor,