Mbonea commited on
Commit
8a4825d
β€’
1 Parent(s): cee6d60

dynamic assests and cutscences -- not tested

Browse files
App/Editor/Schema.py CHANGED
@@ -1,5 +1,6 @@
1
  from typing import List, Optional
2
  from pydantic import BaseModel, HttpUrl
 
3
 
4
 
5
  class LinkInfo(BaseModel):
@@ -7,8 +8,20 @@ class LinkInfo(BaseModel):
7
  link: HttpUrl
8
 
9
 
 
 
 
 
 
 
 
 
 
 
 
10
  class EditorRequest(BaseModel):
11
  links: Optional[List[LinkInfo]] # List of LinkInfo objects
 
12
  script: str
13
 
14
 
 
1
  from typing import List, Optional
2
  from pydantic import BaseModel, HttpUrl
3
+ from pydantic import validator
4
 
5
 
6
  class LinkInfo(BaseModel):
 
8
  link: HttpUrl
9
 
10
 
11
+ class Assets(BaseModel):
12
+ type: str
13
+ sequence: List[dict]
14
+
15
+ @validator("type")
16
+ def valid_type(cls, v):
17
+ if v not in ["video", "audio", "text"]:
18
+ raise ValueError("Invalid asset type")
19
+ return v
20
+
21
+
22
  class EditorRequest(BaseModel):
23
  links: Optional[List[LinkInfo]] # List of LinkInfo objects
24
+ assets: List[Assets]
25
  script: str
26
 
27
 
App/Worker.py CHANGED
@@ -4,9 +4,11 @@ import uuid
4
  from urllib.parse import urlparse
5
  from App import celery_config, bot
6
  from typing import List
7
- from App.Editor.Schema import EditorRequest, LinkInfo
8
  from celery.signals import worker_process_init
9
  from asgiref.sync import async_to_sync
 
 
10
 
11
  celery = Celery()
12
  celery.config_from_object(celery_config)
@@ -21,6 +23,22 @@ def worker_process_init_handler(**kwargs):
21
  bot.start()
22
 
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  def create_symlink(source_dir, target_dir, symlink_name):
25
  source_path = os.path.join(source_dir, symlink_name)
26
  target_path = os.path.join(target_dir, symlink_name)
@@ -83,10 +101,12 @@ def celery_task(video_task: EditorRequest):
83
  project_id = str(uuid.uuid4())
84
  temp_dir = f"/tmp/{project_id}"
85
  output_dir = f"/tmp/{project_id}/out/video.mp4"
 
86
 
87
  chain(
88
  copy_remotion_app.si(remotion_app_dir, temp_dir),
89
  install_dependencies.si(temp_dir),
 
90
  download_assets.si(video_task.links, temp_dir) if video_task.links else None,
91
  render_video.si(temp_dir, output_dir),
92
  cleanup_temp_directory.si(temp_dir, output_dir),
 
4
  from urllib.parse import urlparse
5
  from App import celery_config, bot
6
  from typing import List
7
+ from App.Editor.Schema import EditorRequest, LinkInfo, Assets
8
  from celery.signals import worker_process_init
9
  from asgiref.sync import async_to_sync
10
+ import json
11
+ import os
12
 
13
  celery = Celery()
14
  celery.config_from_object(celery_config)
 
23
  bot.start()
24
 
25
 
26
+ @celery.task
27
+ def create_json_file(assets: List[Assets], asset_dir: str):
28
+ for asset in assets:
29
+ filename = f"{asset.type}Sequences.json"
30
+ filename = filename.capitalize()
31
+ # Convert dictionary to JSON string
32
+ json_string = json.dumps(asset.sequence)
33
+
34
+ # Create directory if it doesn't exist
35
+ os.makedirs(asset_dir, exist_ok=True)
36
+
37
+ # Write JSON string to file
38
+ with open(os.path.join(asset_dir, filename), "w") as f:
39
+ f.write(json_string)
40
+
41
+
42
  def create_symlink(source_dir, target_dir, symlink_name):
43
  source_path = os.path.join(source_dir, symlink_name)
44
  target_path = os.path.join(target_dir, symlink_name)
 
101
  project_id = str(uuid.uuid4())
102
  temp_dir = f"/tmp/{project_id}"
103
  output_dir = f"/tmp/{project_id}/out/video.mp4"
104
+ assets_dir = os.path.join(temp_dir, "src/Assets")
105
 
106
  chain(
107
  copy_remotion_app.si(remotion_app_dir, temp_dir),
108
  install_dependencies.si(temp_dir),
109
+ create_json_file.si(video_task.assets, assets_dir),
110
  download_assets.si(video_task.links, temp_dir) if video_task.links else None,
111
  render_video.si(temp_dir, output_dir),
112
  cleanup_temp_directory.si(temp_dir, output_dir),
Remotion-app/src/HelloWorld/Assets/AudioSequences.json ADDED
@@ -0,0 +1 @@
 
 
1
+ []
Remotion-app/src/HelloWorld/{Transcription.json β†’ Assets/TextSequences.json} RENAMED
File without changes
Remotion-app/src/HelloWorld/Assets/VideoSequences.json ADDED
@@ -0,0 +1 @@
 
 
1
+ []
Remotion-app/src/HelloWorld/AudioStream.jsx ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {Series} from 'remotion';
2
+ import React from 'react';
3
+ import {staticFile, useVideoConfig} from 'remotion';
4
+ import audioSequences from './Assets/AudioSequences.json';
5
+ export default function AudioStream() {
6
+ const {fps} = useVideoConfig();
7
+ return (
8
+ <Series
9
+ style={{
10
+ color: 'white',
11
+ position: 'absolute',
12
+ zIndex: 0,
13
+ }}
14
+ >
15
+ {audioSequences.map((entry, index) => {
16
+ return (
17
+ <Series.Sequence
18
+ from={fps * entry.start}
19
+ durationInFrames={fps * (entry.end - entry.start)}
20
+ >
21
+ <Audio {...entry.props} src={staticFile(entry.name)} />
22
+ </Series.Sequence>
23
+ );
24
+ })}
25
+ </Series>
26
+ );
27
+ }
Remotion-app/src/HelloWorld/{Subtitle.jsx β†’ TextStream.jsx} RENAMED
@@ -1,24 +1,23 @@
1
  import React, {createElement} from 'react';
2
  import {useVideoConfig} from 'remotion';
3
- import {loadFont as loadMain} from '@remotion/google-fonts/Bungee';
4
- import transcriptData from './Transcription.json';
5
- const {fontFamily: fontFamily2} = loadMain();
6
  import {TransitionSeries} from '@remotion/transitions';
7
 
8
  const codeStyle = (index) => {
9
  return {
10
  color: 'white',
11
  position: 'absolute',
12
- backgroundColor: 'red',
13
  top: '50%',
14
- width: '100%',
15
  transform: 'translate(-50%, -50%)',
16
  left: '50%',
17
  };
18
  };
19
 
20
  const subtitle = {
21
- fontFamily: fontFamily2,
22
  fontSize: 120,
23
  textAlign: 'center',
24
  display: 'relative',
@@ -26,22 +25,31 @@ const subtitle = {
26
  width: '100%',
27
  };
28
 
29
- export const Subtitle = () => {
 
 
 
 
 
 
 
30
  const {fps} = useVideoConfig();
31
 
32
  return (
33
- <div style={subtitle}>
34
  <TransitionSeries>
35
  {transcriptData.map((entry, index) => {
36
  return (
37
- <TransitionSeries.Sequence
38
- from={entry.start * fps}
39
- durationInFrames={fps * (entry.end - entry.start)}
40
- >
41
- <Letter index={index} color="#0b84f3">
42
- {entry.text}
43
- </Letter>
44
- </TransitionSeries.Sequence>
 
 
45
  );
46
  })}
47
  </TransitionSeries>
 
1
  import React, {createElement} from 'react';
2
  import {useVideoConfig} from 'remotion';
3
+ import * as Fonts from '@remotion/google-fonts';
4
+ import transcriptData from './Assets/TextSequences.json';
5
+ import {FONT_FAMILY} from './constants';
6
  import {TransitionSeries} from '@remotion/transitions';
7
 
8
  const codeStyle = (index) => {
9
  return {
10
  color: 'white',
11
  position: 'absolute',
 
12
  top: '50%',
13
+ zIndex: 50,
14
  transform: 'translate(-50%, -50%)',
15
  left: '50%',
16
  };
17
  };
18
 
19
  const subtitle = {
20
+ fontFamily: FONT_FAMILY,
21
  fontSize: 120,
22
  textAlign: 'center',
23
  display: 'relative',
 
25
  width: '100%',
26
  };
27
 
28
+ Fonts.getAvailableFonts()
29
+ .filter((font) => {
30
+ return font.fontFamily === FONT_FAMILY;
31
+ })[0]
32
+ .load()
33
+ .then((font) => font.loadFont());
34
+
35
+ export const TextStream = () => {
36
  const {fps} = useVideoConfig();
37
 
38
  return (
39
+ <div style={{...temp, ...{}}}>
40
  <TransitionSeries>
41
  {transcriptData.map((entry, index) => {
42
  return (
43
+ <>
44
+ <TransitionSeries.Sequence
45
+ from={entry.start * fps}
46
+ durationInFrames={fps * (entry.end - entry.start)}
47
+ >
48
+ <Letter index={index} color="#0b84f3">
49
+ {entry.text}
50
+ </Letter>
51
+ </TransitionSeries.Sequence>
52
+ </>
53
  );
54
  })}
55
  </TransitionSeries>
Remotion-app/src/HelloWorld/VideoStream.jsx ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {Series} from 'remotion';
2
+ import React from 'react';
3
+ import {Video, staticFile, useVideoConfig} from 'remotion';
4
+ import videoSequences from './Assets/VideoSequences.json';
5
+ export default function VideoStream() {
6
+ const {fps} = useVideoConfig();
7
+ return (
8
+ <Series
9
+ style={{
10
+ color: 'white',
11
+ position: 'absolute',
12
+ zIndex: 0,
13
+ }}
14
+ >
15
+ {videoSequences.map((entry, index) => {
16
+ return (
17
+ <Series.Sequence
18
+ from={fps * entry.start}
19
+ durationInFrames={fps * (entry.end - entry.start)}
20
+ >
21
+ <Video {...entry.props} src={staticFile(entry.name)} />
22
+ </Series.Sequence>
23
+ );
24
+ })}
25
+ </Series>
26
+ );
27
+ }
Remotion-app/src/HelloWorld/constants.js CHANGED
@@ -3,4 +3,6 @@
3
  export const COLOR_1 = '#86A8E7';
4
  export const COLOR_2 = '#91EAE4';
5
 
6
- export const FONT_FAMILY = 'SF Pro Text, Helvetica, Arial, sans-serif';
 
 
 
3
  export const COLOR_1 = '#86A8E7';
4
  export const COLOR_2 = '#91EAE4';
5
 
6
+ export const FONT_FAMILY = 'Bungee';
7
+ export const FPS = 30;
8
+ export const DURATION = 30 * FPS; // 30 seconds
Remotion-app/src/HelloWorld/index.jsx CHANGED
@@ -1,48 +1,14 @@
1
- import {Subtitle} from './Subtitle';
2
- import {
3
- spring,
4
- AbsoluteFill,
5
- useCurrentFrame,
6
- useVideoConfig,
7
- Audio,
8
- staticFile,
9
- Video,
10
- } from 'remotion';
11
- import {preloadAudio, resolveRedirect} from '@remotion/preload';
12
-
13
- export const HelloWorld = ({titleText, titleColor}) => {
14
- const frame = useCurrentFrame();
15
- const {fps} = useVideoConfig();
16
- const scale = spring({
17
- fps,
18
- frame,
19
- config: {
20
- damping: 200,
21
- mass: 0.5,
22
- stiffness: 200,
23
- overshootClamping: false,
24
- restDisplacementThreshold: 0.01,
25
- restSpeedThreshold: 0.01,
26
- },
27
- });
28
 
 
29
  return (
30
  <AbsoluteFill style={{position: 'relative', backgroundColor: 'black'}}>
31
- <Audio
32
- volume={0.15}
33
- src={staticFile('background.mp3')}
34
- // src={'https://yakova-streamer.hf.space/download/20707'}
35
- />
36
-
37
- {/* <Video
38
- src={
39
- 'https://player.vimeo.com/external/514185553.hd.mp4?s=33cb766901019185385a757eab89a9fd0d50d0c0&profile_id=172&oauth2_token_id=57447761'
40
- }
41
- /> */}
42
- {/* <Audio src={'https://yakova-streamer.hf.space/download/20711'} /> */}
43
- {/* <img src={''} style={{transform: `scale(${scale})`}} /> */}
44
-
45
- <Subtitle />
46
  </AbsoluteFill>
47
  );
48
  };
 
1
+ import {AbsoluteFill} from 'remotion';
2
+ import VideoStream from './VideoStream';
3
+ import TextStream from './TextStream';
4
+ import AudioStream from './AudioStream';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
+ export const HelloWorld = () => {
7
  return (
8
  <AbsoluteFill style={{position: 'relative', backgroundColor: 'black'}}>
9
+ <TextStream />
10
+ <VideoStream />
11
+ <AudioStream />
 
 
 
 
 
 
 
 
 
 
 
 
12
  </AbsoluteFill>
13
  );
14
  };
Remotion-app/src/Root.jsx CHANGED
@@ -1,5 +1,6 @@
1
  import {Composition} from 'remotion';
2
  import {HelloWorld} from './HelloWorld';
 
3
 
4
  export const RemotionRoot = () => {
5
  return (
@@ -7,14 +8,10 @@ export const RemotionRoot = () => {
7
  <Composition
8
  id="HelloWorld"
9
  component={HelloWorld}
10
- durationInFrames={15000}
11
  fps={30}
12
  height={1920}
13
  width={1080}
14
- defaultProps={{
15
- titleText: 'Welcome to Remotion',
16
- titleColor: 'black',
17
- }}
18
  />
19
  </>
20
  );
 
1
  import {Composition} from 'remotion';
2
  import {HelloWorld} from './HelloWorld';
3
+ import {DURATION} from './HelloWorld/constants';
4
 
5
  export const RemotionRoot = () => {
6
  return (
 
8
  <Composition
9
  id="HelloWorld"
10
  component={HelloWorld}
11
+ durationInFrames={DURATION}
12
  fps={30}
13
  height={1920}
14
  width={1080}
 
 
 
 
15
  />
16
  </>
17
  );