@hed-hog/studio 0.0.285

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/index.d.ts +4 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +20 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/studio.controller.d.ts +79 -0
  6. package/dist/studio.controller.d.ts.map +1 -0
  7. package/dist/studio.controller.js +186 -0
  8. package/dist/studio.controller.js.map +1 -0
  9. package/dist/studio.module.d.ts +3 -0
  10. package/dist/studio.module.d.ts.map +1 -0
  11. package/dist/studio.module.js +33 -0
  12. package/dist/studio.module.js.map +1 -0
  13. package/dist/studio.service.d.ts +76 -0
  14. package/dist/studio.service.d.ts.map +1 -0
  15. package/dist/studio.service.js +98 -0
  16. package/dist/studio.service.js.map +1 -0
  17. package/hedhog/data/menu.yaml +114 -0
  18. package/hedhog/data/role.yaml +7 -0
  19. package/hedhog/data/route.yaml +152 -0
  20. package/hedhog/data/setting_group.yaml +8 -0
  21. package/hedhog/frontend/app/_components/studio-status-badge.tsx.ejs +14 -0
  22. package/hedhog/frontend/app/_lib/mocks.ts.ejs +209 -0
  23. package/hedhog/frontend/app/_lib/status.ts.ejs +38 -0
  24. package/hedhog/frontend/app/_lib/types.ts.ejs +148 -0
  25. package/hedhog/frontend/app/assets/page.tsx.ejs +117 -0
  26. package/hedhog/frontend/app/editing/page.tsx.ejs +55 -0
  27. package/hedhog/frontend/app/incidents/page.tsx.ejs +59 -0
  28. package/hedhog/frontend/app/page.tsx.ejs +207 -0
  29. package/hedhog/frontend/app/projects/[id]/page.tsx.ejs +323 -0
  30. package/hedhog/frontend/app/projects/form/page.tsx.ejs +129 -0
  31. package/hedhog/frontend/app/projects/page.tsx.ejs +169 -0
  32. package/hedhog/frontend/app/publication/page.tsx.ejs +62 -0
  33. package/hedhog/frontend/app/scenes/form/page.tsx.ejs +110 -0
  34. package/hedhog/frontend/app/sessions/[id]/page.tsx.ejs +118 -0
  35. package/hedhog/frontend/app/sessions/form/page.tsx.ejs +103 -0
  36. package/hedhog/frontend/app/sessions/page.tsx.ejs +80 -0
  37. package/hedhog/frontend/app/storage-profiles/form/page.tsx.ejs +100 -0
  38. package/hedhog/frontend/app/storage-profiles/page.tsx.ejs +80 -0
  39. package/hedhog/frontend/app/takes/[id]/page.tsx.ejs +143 -0
  40. package/hedhog/frontend/app/takes/page.tsx.ejs +74 -0
  41. package/hedhog/table/capture_agent.yaml +48 -0
  42. package/hedhog/table/edit_composition.yaml +67 -0
  43. package/hedhog/table/edit_pipeline.yaml +46 -0
  44. package/hedhog/table/edit_stage_execution.yaml +46 -0
  45. package/hedhog/table/editor_delivery_package.yaml +63 -0
  46. package/hedhog/table/ingestion_job.yaml +74 -0
  47. package/hedhog/table/media_asset.yaml +130 -0
  48. package/hedhog/table/participant_command_ack.yaml +32 -0
  49. package/hedhog/table/participant_recording.yaml +67 -0
  50. package/hedhog/table/production_binding.yaml +34 -0
  51. package/hedhog/table/production_project.yaml +104 -0
  52. package/hedhog/table/publication_target.yaml +44 -0
  53. package/hedhog/table/recorded_file.yaml +74 -0
  54. package/hedhog/table/recording_command.yaml +72 -0
  55. package/hedhog/table/recording_incident.yaml +65 -0
  56. package/hedhog/table/recording_session.yaml +86 -0
  57. package/hedhog/table/scene.yaml +64 -0
  58. package/hedhog/table/scene_take.yaml +83 -0
  59. package/hedhog/table/session_participant.yaml +55 -0
  60. package/hedhog/table/storage_profile.yaml +75 -0
  61. package/hedhog/table/sync_marker.yaml +44 -0
  62. package/package.json +38 -0
  63. package/src/index.ts +4 -0
  64. package/src/language/en.json +8 -0
  65. package/src/language/pt.json +8 -0
  66. package/src/studio.controller.ts +94 -0
  67. package/src/studio.module.ts +20 -0
  68. package/src/studio.service.ts +102 -0
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+
3
+ import { Page, PageHeader } from '@/components/entity-list';
4
+ import type { PageHeaderProps } from '@/components/entity-list/page-header';
5
+ import { Button } from '@/components/ui/button';
6
+ import {
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ } from '@/components/ui/table';
14
+ import Link from 'next/link';
15
+ import { StudioStatusBadge } from '../_components/studio-status-badge';
16
+ import { STUDIO_SESSIONS } from '../_lib/mocks';
17
+
18
+ export default function StudioSessionsPage() {
19
+ const breadcrumbs: PageHeaderProps['breadcrumbs'] = [
20
+ { label: 'Home', href: '/' },
21
+ { label: 'Studio', href: '/studio' },
22
+ { label: 'Sessoes' },
23
+ ];
24
+
25
+ return (
26
+ <Page>
27
+ <PageHeader
28
+ title="Sessoes"
29
+ description="Sessoes de gravacao local e colaborativa"
30
+ breadcrumbs={breadcrumbs as { label: string; href?: string }[]}
31
+ actions={
32
+ <Button asChild size="sm">
33
+ <Link href="/studio/sessions/form">Nova Sessao</Link>
34
+ </Button>
35
+ }
36
+ />
37
+
38
+ <div className="rounded-md border">
39
+ <Table>
40
+ <TableHeader>
41
+ <TableRow>
42
+ <TableHead>Titulo</TableHead>
43
+ <TableHead>Status</TableHead>
44
+ <TableHead>Tipo</TableHead>
45
+ <TableHead>Host</TableHead>
46
+ <TableHead>Participantes</TableHead>
47
+ <TableHead>Room</TableHead>
48
+ <TableHead>Data/Hora</TableHead>
49
+ <TableHead className="text-right">Acoes</TableHead>
50
+ </TableRow>
51
+ </TableHeader>
52
+ <TableBody>
53
+ {STUDIO_SESSIONS.map((session) => (
54
+ <TableRow key={session.id}>
55
+ <TableCell className="font-medium">{session.title}</TableCell>
56
+ <TableCell>
57
+ <StudioStatusBadge value={session.status} />
58
+ </TableCell>
59
+ <TableCell>{session.sessionType}</TableCell>
60
+ <TableCell>{session.host}</TableCell>
61
+ <TableCell>{session.participants}</TableCell>
62
+ <TableCell>{session.roomKey}</TableCell>
63
+ <TableCell>
64
+ {new Date(session.startedAt).toLocaleString('pt-BR')}
65
+ </TableCell>
66
+ <TableCell className="text-right">
67
+ <Button asChild size="sm" variant="outline">
68
+ <Link href={`/studio/sessions/${session.id}`}>
69
+ Abrir Sessao
70
+ </Link>
71
+ </Button>
72
+ </TableCell>
73
+ </TableRow>
74
+ ))}
75
+ </TableBody>
76
+ </Table>
77
+ </div>
78
+ </Page>
79
+ );
80
+ }
@@ -0,0 +1,100 @@
1
+ 'use client';
2
+
3
+ import { Page, PageHeader } from '@/components/entity-list';
4
+ import { Button } from '@/components/ui/button';
5
+ import {
6
+ Form,
7
+ FormControl,
8
+ FormField,
9
+ FormItem,
10
+ FormLabel,
11
+ FormMessage,
12
+ } from '@/components/ui/form';
13
+ import { Input } from '@/components/ui/input';
14
+ import { zodResolver } from '@hookform/resolvers/zod';
15
+ import { useForm } from 'react-hook-form';
16
+ import { z } from 'zod';
17
+
18
+ const schema = z.object({
19
+ name: z.string().min(2),
20
+ provider: z.string().min(1),
21
+ bucket: z.string().min(1),
22
+ region: z.string().min(1),
23
+ endpointUrl: z.string().optional(),
24
+ basePath: z.string().optional(),
25
+ pathTemplate: z.string().optional(),
26
+ accessKey: z.string().optional(),
27
+ secretKey: z.string().optional(),
28
+ });
29
+
30
+ export default function StudioStorageProfileFormPage() {
31
+ const form = useForm<z.infer<typeof schema>>({
32
+ resolver: zodResolver(schema),
33
+ defaultValues: {
34
+ name: '',
35
+ provider: 's3',
36
+ bucket: '',
37
+ region: 'us-east-1',
38
+ endpointUrl: '',
39
+ basePath: 'studio/raw',
40
+ pathTemplate: '{project}/{take}/{participant}',
41
+ accessKey: '',
42
+ secretKey: '',
43
+ },
44
+ });
45
+
46
+ return (
47
+ <Page>
48
+ <PageHeader
49
+ title="Formulario de Storage Profile"
50
+ description="Cadastro estatico de bucket para ingestao"
51
+ breadcrumbs={[
52
+ { label: 'Home', href: '/' },
53
+ { label: 'Studio', href: '/studio' },
54
+ { label: 'Storage Profile' },
55
+ ]}
56
+ />
57
+
58
+ <Form {...form}>
59
+ <form
60
+ className="grid gap-4"
61
+ onSubmit={form.handleSubmit(() => undefined)}
62
+ >
63
+ {[
64
+ ['name', 'Nome', 'studio-main'],
65
+ ['provider', 'Provider', 's3 | minio | r2'],
66
+ ['bucket', 'Bucket', 'hedhog-studio-prod'],
67
+ ['region', 'Regiao', 'us-east-1'],
68
+ ['endpointUrl', 'Endpoint URL', 'https://...'],
69
+ ['basePath', 'Path base', 'studio/raw'],
70
+ [
71
+ 'pathTemplate',
72
+ 'Template de caminho',
73
+ '{project}/{take}/{participant}',
74
+ ],
75
+ ['accessKey', 'Access key', '***'],
76
+ ['secretKey', 'Secret key', '***'],
77
+ ].map(([name, label, placeholder]) => (
78
+ <FormField
79
+ key={name}
80
+ control={form.control}
81
+ name={name as keyof z.infer<typeof schema>}
82
+ render={({ field }) => (
83
+ <FormItem>
84
+ <FormLabel>{label}</FormLabel>
85
+ <FormControl>
86
+ <Input placeholder={placeholder} {...field} />
87
+ </FormControl>
88
+ <FormMessage />
89
+ </FormItem>
90
+ )}
91
+ />
92
+ ))}
93
+ <Button type="submit" className="w-full">
94
+ Salvar Storage Profile
95
+ </Button>
96
+ </form>
97
+ </Form>
98
+ </Page>
99
+ );
100
+ }
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+
3
+ import { Page, PageHeader } from '@/components/entity-list';
4
+ import type { PageHeaderProps } from '@/components/entity-list/page-header';
5
+ import { Button } from '@/components/ui/button';
6
+ import {
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ } from '@/components/ui/table';
14
+ import Link from 'next/link';
15
+ import { StudioStatusBadge } from '../_components/studio-status-badge';
16
+ import { STUDIO_STORAGE_PROFILES } from '../_lib/mocks';
17
+
18
+ export default function StudioStorageProfilesPage() {
19
+ const breadcrumbs: PageHeaderProps['breadcrumbs'] = [
20
+ { label: 'Home', href: '/' },
21
+ { label: 'Studio', href: '/studio' },
22
+ { label: 'Storage Profiles' },
23
+ ];
24
+
25
+ return (
26
+ <Page>
27
+ <PageHeader
28
+ title="Storage Profiles"
29
+ description="Buckets e configuracoes de ingestao"
30
+ breadcrumbs={breadcrumbs as { label: string; href?: string }[]}
31
+ actions={
32
+ <Button asChild size="sm">
33
+ <Link href="/studio/storage-profiles/form">
34
+ Novo Storage Profile
35
+ </Link>
36
+ </Button>
37
+ }
38
+ />
39
+
40
+ <div className="rounded-md border">
41
+ <Table>
42
+ <TableHeader>
43
+ <TableRow>
44
+ <TableHead>Nome</TableHead>
45
+ <TableHead>Provider</TableHead>
46
+ <TableHead>Bucket</TableHead>
47
+ <TableHead>Regiao</TableHead>
48
+ <TableHead>Path Base</TableHead>
49
+ <TableHead>Status</TableHead>
50
+ <TableHead>Ultimo teste</TableHead>
51
+ <TableHead className="text-right">Acoes</TableHead>
52
+ </TableRow>
53
+ </TableHeader>
54
+ <TableBody>
55
+ {STUDIO_STORAGE_PROFILES.map((profile) => (
56
+ <TableRow key={profile.id}>
57
+ <TableCell>{profile.name}</TableCell>
58
+ <TableCell>{profile.provider}</TableCell>
59
+ <TableCell>{profile.bucket}</TableCell>
60
+ <TableCell>{profile.region}</TableCell>
61
+ <TableCell>{profile.basePath}</TableCell>
62
+ <TableCell>
63
+ <StudioStatusBadge value={profile.status} />
64
+ </TableCell>
65
+ <TableCell>
66
+ {new Date(profile.lastTestedAt).toLocaleString('pt-BR')}
67
+ </TableCell>
68
+ <TableCell className="text-right">
69
+ <Button size="sm" variant="outline">
70
+ Testar conexao
71
+ </Button>
72
+ </TableCell>
73
+ </TableRow>
74
+ ))}
75
+ </TableBody>
76
+ </Table>
77
+ </div>
78
+ </Page>
79
+ );
80
+ }
@@ -0,0 +1,143 @@
1
+ 'use client';
2
+
3
+ import { Page, PageHeader } from '@/components/entity-list';
4
+ import type { PageHeaderProps } from '@/components/entity-list/page-header';
5
+ import { Button } from '@/components/ui/button';
6
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
7
+ import {
8
+ Table,
9
+ TableBody,
10
+ TableCell,
11
+ TableHead,
12
+ TableHeader,
13
+ TableRow,
14
+ } from '@/components/ui/table';
15
+ import Link from 'next/link';
16
+ import { useParams } from 'next/navigation';
17
+ import { StudioStatusBadge } from '../../_components/studio-status-badge';
18
+ import {
19
+ STUDIO_ASSETS,
20
+ STUDIO_INCIDENTS,
21
+ STUDIO_TAKES,
22
+ } from '../../_lib/mocks';
23
+
24
+ export default function StudioTakeDetailPage() {
25
+ const params = useParams<{ id: string }>();
26
+ const take = STUDIO_TAKES.find((item) => item.id === Number(params.id));
27
+
28
+ if (!take) {
29
+ return null;
30
+ }
31
+
32
+ const currentTake = take!;
33
+
34
+ const breadcrumbs: PageHeaderProps['breadcrumbs'] = [
35
+ { label: 'Home', href: '/' },
36
+ { label: 'Studio', href: '/studio' },
37
+ { label: 'Takes', href: '/studio/takes' },
38
+ { label: `Take #${currentTake.id}` },
39
+ ];
40
+
41
+ const assets = STUDIO_ASSETS.filter((item) => item.takeId === currentTake.id);
42
+
43
+ return (
44
+ <Page>
45
+ <PageHeader
46
+ title={`Take #${currentTake.id}`}
47
+ description="Detalhamento tecnico do take"
48
+ breadcrumbs={breadcrumbs as { label: string; href?: string }[]}
49
+ actions={
50
+ <Button asChild size="sm">
51
+ <Link href="/studio/editing">Gerar pacote para editor</Link>
52
+ </Button>
53
+ }
54
+ />
55
+
56
+ <div className="grid gap-4 lg:grid-cols-3">
57
+ <Card>
58
+ <CardHeader>
59
+ <CardTitle className="text-base">Resumo</CardTitle>
60
+ </CardHeader>
61
+ <CardContent className="space-y-2 text-sm">
62
+ <div>Cena: {currentTake.sceneName}</div>
63
+ <div>Sessao: {currentTake.sessionTitle}</div>
64
+ <div>
65
+ Status: <StudioStatusBadge value={currentTake.status} />
66
+ </div>
67
+ <div>
68
+ Upload: {currentTake.uploadedFiles}/{currentTake.expectedFiles}
69
+ </div>
70
+ <div>
71
+ Integridade: <StudioStatusBadge value={currentTake.integrity} />
72
+ </div>
73
+ </CardContent>
74
+ </Card>
75
+ <Card className="lg:col-span-2">
76
+ <CardHeader>
77
+ <CardTitle className="text-base">
78
+ Marcadores de sincronizacao
79
+ </CardTitle>
80
+ </CardHeader>
81
+ <CardContent className="space-y-2 text-sm">
82
+ <div className="rounded-md border p-2">00:00:00 - clap</div>
83
+ <div className="rounded-md border p-2">
84
+ 00:01:20 - manual marker
85
+ </div>
86
+ <div className="rounded-md border p-2">00:06:45 - take_stop</div>
87
+ </CardContent>
88
+ </Card>
89
+ </div>
90
+
91
+ <Card>
92
+ <CardHeader>
93
+ <CardTitle className="text-base">Arquivos gerados</CardTitle>
94
+ </CardHeader>
95
+ <CardContent>
96
+ <Table>
97
+ <TableHeader>
98
+ <TableRow>
99
+ <TableHead>Participante</TableHead>
100
+ <TableHead>Tipo</TableHead>
101
+ <TableHead>Tamanho</TableHead>
102
+ <TableHead>Duracao</TableHead>
103
+ <TableHead>Disponibilidade</TableHead>
104
+ </TableRow>
105
+ </TableHeader>
106
+ <TableBody>
107
+ {assets.map((asset) => (
108
+ <TableRow key={asset.id}>
109
+ <TableCell>{asset.participant}</TableCell>
110
+ <TableCell>{asset.sourceType}</TableCell>
111
+ <TableCell>{asset.sizeMb} MB</TableCell>
112
+ <TableCell>{asset.durationSec}s</TableCell>
113
+ <TableCell>
114
+ <StudioStatusBadge value={asset.availability} />
115
+ </TableCell>
116
+ </TableRow>
117
+ ))}
118
+ </TableBody>
119
+ </Table>
120
+ </CardContent>
121
+ </Card>
122
+
123
+ <Card>
124
+ <CardHeader>
125
+ <CardTitle className="text-base">Incidentes vinculados</CardTitle>
126
+ </CardHeader>
127
+ <CardContent className="space-y-2 text-sm">
128
+ {STUDIO_INCIDENTS.map((incident) => (
129
+ <div
130
+ key={incident.id}
131
+ className="flex items-center justify-between rounded-md border p-2"
132
+ >
133
+ <div>
134
+ {incident.type} - {incident.target}
135
+ </div>
136
+ <StudioStatusBadge value={incident.severity} />
137
+ </div>
138
+ ))}
139
+ </CardContent>
140
+ </Card>
141
+ </Page>
142
+ );
143
+ }
@@ -0,0 +1,74 @@
1
+ 'use client';
2
+
3
+ import { Page, PageHeader } from '@/components/entity-list';
4
+ import {
5
+ Table,
6
+ TableBody,
7
+ TableCell,
8
+ TableHead,
9
+ TableHeader,
10
+ TableRow,
11
+ } from '@/components/ui/table';
12
+ import { Button } from '@/components/ui/button';
13
+ import Link from 'next/link';
14
+ import { STUDIO_TAKES } from '../_lib/mocks';
15
+ import { StudioStatusBadge } from '../_components/studio-status-badge';
16
+
17
+ export default function StudioTakesPage() {
18
+ return (
19
+ <Page>
20
+ <PageHeader
21
+ title="Takes"
22
+ description="Controle de takes por cena e sessao"
23
+ breadcrumbs={[
24
+ { label: 'Home', href: '/' },
25
+ { label: 'Studio', href: '/studio' },
26
+ { label: 'Takes' },
27
+ ]}
28
+ />
29
+
30
+ <div className="rounded-md border">
31
+ <Table>
32
+ <TableHeader>
33
+ <TableRow>
34
+ <TableHead>Take</TableHead>
35
+ <TableHead>Cena</TableHead>
36
+ <TableHead>Sessao</TableHead>
37
+ <TableHead>Status</TableHead>
38
+ <TableHead>Participantes</TableHead>
39
+ <TableHead>Arquivos</TableHead>
40
+ <TableHead>Integridade</TableHead>
41
+ <TableHead>Selecionado</TableHead>
42
+ <TableHead className="text-right">Acoes</TableHead>
43
+ </TableRow>
44
+ </TableHeader>
45
+ <TableBody>
46
+ {STUDIO_TAKES.map((take) => (
47
+ <TableRow key={take.id}>
48
+ <TableCell className="font-medium">#{take.id}</TableCell>
49
+ <TableCell>{take.sceneName}</TableCell>
50
+ <TableCell>{take.sessionTitle}</TableCell>
51
+ <TableCell>
52
+ <StudioStatusBadge value={take.status} />
53
+ </TableCell>
54
+ <TableCell>{take.participants}</TableCell>
55
+ <TableCell>
56
+ {take.uploadedFiles}/{take.expectedFiles}
57
+ </TableCell>
58
+ <TableCell>
59
+ <StudioStatusBadge value={take.integrity} />
60
+ </TableCell>
61
+ <TableCell>{take.selected ? 'Sim' : 'Nao'}</TableCell>
62
+ <TableCell className="text-right">
63
+ <Button asChild size="sm" variant="outline">
64
+ <Link href={`/studio/takes/${take.id}`}>Detalhar</Link>
65
+ </Button>
66
+ </TableCell>
67
+ </TableRow>
68
+ ))}
69
+ </TableBody>
70
+ </Table>
71
+ </div>
72
+ </Page>
73
+ );
74
+ }
@@ -0,0 +1,48 @@
1
+ columns:
2
+ - type: pk
3
+ - name: session_participant_id
4
+ type: fk
5
+ references:
6
+ table: session_participant
7
+ column: id
8
+ onDelete: CASCADE
9
+ onUpdate: CASCADE
10
+ - name: agent_type
11
+ type: enum
12
+ values: [desktop_app, browser, mobile]
13
+ default: desktop_app
14
+ - name: agent_version
15
+ type: varchar
16
+ length: 50
17
+ isNullable: true
18
+ - name: platform
19
+ type: varchar
20
+ length: 50
21
+ isNullable: true
22
+ - name: platform_version
23
+ type: varchar
24
+ length: 50
25
+ isNullable: true
26
+ - name: device_name
27
+ type: varchar
28
+ length: 100
29
+ isNullable: true
30
+ - name: hostname
31
+ type: varchar
32
+ length: 100
33
+ isNullable: true
34
+ - name: capabilities_json
35
+ type: json
36
+ isNullable: true
37
+ - name: is_active
38
+ type: boolean
39
+ default: true
40
+ - name: last_seen_at
41
+ type: datetime
42
+ isNullable: true
43
+ - type: created_at
44
+ - type: updated_at
45
+
46
+ indices:
47
+ - columns: [session_participant_id]
48
+ - columns: [is_active]
@@ -0,0 +1,67 @@
1
+ columns:
2
+ - type: pk
3
+ - name: production_project_id
4
+ type: fk
5
+ references:
6
+ table: production_project
7
+ column: id
8
+ onDelete: CASCADE
9
+ onUpdate: CASCADE
10
+ - name: scene_id
11
+ type: fk
12
+ isNullable: true
13
+ references:
14
+ table: scene
15
+ column: id
16
+ onDelete: SET NULL
17
+ onUpdate: CASCADE
18
+ - name: source_scene_take_id
19
+ type: fk
20
+ isNullable: true
21
+ references:
22
+ table: scene_take
23
+ column: id
24
+ onDelete: SET NULL
25
+ onUpdate: CASCADE
26
+ - name: composition_type
27
+ type: enum
28
+ values: [single_source, multi_participant, picture_in_picture, split_screen]
29
+ default: single_source
30
+ - name: primary_audio_source
31
+ type: varchar
32
+ length: 120
33
+ isNullable: true
34
+ - name: timeline_definition_json
35
+ type: json
36
+ isNullable: true
37
+ - name: status
38
+ type: enum
39
+ values: [draft, ready, in_editing, finalized]
40
+ default: draft
41
+ - name: notes
42
+ type: text
43
+ isNullable: true
44
+ - name: created_by_user_id
45
+ type: fk
46
+ isNullable: true
47
+ references:
48
+ table: user
49
+ column: id
50
+ onDelete: SET NULL
51
+ onUpdate: CASCADE
52
+ - name: updated_by_user_id
53
+ type: fk
54
+ isNullable: true
55
+ references:
56
+ table: user
57
+ column: id
58
+ onDelete: SET NULL
59
+ onUpdate: CASCADE
60
+ - type: created_at
61
+ - type: updated_at
62
+
63
+ indices:
64
+ - columns: [production_project_id]
65
+ - columns: [scene_id]
66
+ - columns: [source_scene_take_id]
67
+ - columns: [status]
@@ -0,0 +1,46 @@
1
+ columns:
2
+ - type: pk
3
+ - name: production_project_id
4
+ type: fk
5
+ references:
6
+ table: production_project
7
+ column: id
8
+ onDelete: CASCADE
9
+ onUpdate: CASCADE
10
+ - name: current_stage
11
+ type: enum
12
+ values: [preparation, recording, rough_cut, cleanup, finalization, publication]
13
+ default: preparation
14
+ - name: status
15
+ type: enum
16
+ values: [pending, in_progress, blocked, completed, cancelled]
17
+ default: pending
18
+ - name: assigned_editor_user_id
19
+ type: fk
20
+ isNullable: true
21
+ references:
22
+ table: user
23
+ column: id
24
+ onDelete: SET NULL
25
+ onUpdate: CASCADE
26
+ - name: due_date
27
+ type: datetime
28
+ isNullable: true
29
+ - name: started_at
30
+ type: datetime
31
+ isNullable: true
32
+ - name: completed_at
33
+ type: datetime
34
+ isNullable: true
35
+ - name: notes
36
+ type: text
37
+ isNullable: true
38
+ - type: created_at
39
+ - type: updated_at
40
+
41
+ indices:
42
+ - columns: [production_project_id]
43
+ isUnique: true
44
+ - columns: [status]
45
+ - columns: [current_stage]
46
+ - columns: [assigned_editor_user_id]
@@ -0,0 +1,46 @@
1
+ columns:
2
+ - type: pk
3
+ - name: edit_pipeline_id
4
+ type: fk
5
+ references:
6
+ table: edit_pipeline
7
+ column: id
8
+ onDelete: CASCADE
9
+ onUpdate: CASCADE
10
+ - name: stage_name
11
+ type: enum
12
+ values: [preparation, recording, rough_cut, cleanup, finalization, publication]
13
+ - name: stage_order
14
+ type: int
15
+ - name: status
16
+ type: enum
17
+ values: [pending, in_progress, blocked, completed, cancelled]
18
+ default: pending
19
+ - name: assigned_user_id
20
+ type: fk
21
+ isNullable: true
22
+ references:
23
+ table: user
24
+ column: id
25
+ onDelete: SET NULL
26
+ onUpdate: CASCADE
27
+ - name: checklist_json
28
+ type: json
29
+ isNullable: true
30
+ - name: notes
31
+ type: text
32
+ isNullable: true
33
+ - name: started_at
34
+ type: datetime
35
+ isNullable: true
36
+ - name: finished_at
37
+ type: datetime
38
+ isNullable: true
39
+ - type: created_at
40
+ - type: updated_at
41
+
42
+ indices:
43
+ - columns: [edit_pipeline_id, stage_order]
44
+ isUnique: true
45
+ - columns: [status]
46
+ - columns: [assigned_user_id]