Eyuvaraj commited on
Commit
dec3ec5
·
1 Parent(s): 7b45bbe

heygen avatar app

Browse files
Files changed (8) hide show
  1. .gitignore +4 -1
  2. app.py +88 -42
  3. app_v2.py +68 -0
  4. clips_metadata.json +63 -63
  5. dev.ipynb +144 -20
  6. heygen.py +173 -0
  7. sample.jpg +0 -0
  8. utils.py +31 -1
.gitignore CHANGED
@@ -1,4 +1,7 @@
1
  venv/
2
  __pycache__/
3
  output.mp4
4
- UPIBOT.mp4
 
 
 
 
1
  venv/
2
  __pycache__/
3
  output.mp4
4
+ UPIBOT.mp4
5
+ b4319fabebeb43e89b68f00f*
6
+ b4319fabebeb43e89b68f00f584014f1_clips/
7
+ uploaded_photo.*
app.py CHANGED
@@ -1,68 +1,114 @@
1
  import streamlit as st
 
 
 
 
 
2
  from utils import *
3
 
 
 
4
  st.set_page_config(
5
  page_title="Number to Video",
6
  page_icon="🎥",
7
- initial_sidebar_state="collapsed",
8
  )
9
 
10
- # Input number
11
- number = st.text_input("Enter a number between 1 and 99,99,999", "24")
12
- number = int(number)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- if number:
 
 
15
  col1, col2 = st.columns(2)
16
 
17
  with col1:
18
  with st.container(border=True):
19
- st.subheader("Settings")
20
- st.divider()
21
 
22
- avatar = st.radio("Choose an avatar", ("Custom avatar", "Template avatar"))
23
- avatar = "custom" if avatar == "Custom avatar" else "template"
24
 
25
  # Generate clip sequence based on the number
26
  clips = generate_clip_sequence(number)
27
  if clips:
28
  st.write("Generated Clips Sequence:")
 
29
  for i, clip in enumerate(clips):
30
  st.write(f"Clip {i + 1}: {clip}")
31
- submit = st.button("Create Video", use_container_width=True)
32
-
33
- if clips:
34
- with st.sidebar:
35
- st.title("Advanced Settings")
36
- trim_settings = []
37
- for i, clip in enumerate(clips):
38
- with st.container(border=True):
39
- st.write(f"Adjust settings for {clip}:")
40
- length = get_clip_duration(clip, avatar)
41
- default_trim_start = 0.10
42
- default_trim_end = length - 0.10
43
-
44
- # trim_bounds = st.slider(
45
- # f"Trim bounds for {clip}",
46
- # 0.0, length,
47
- # (default_trim_start, default_trim_end),
48
- # 0.01,
49
- # key=f"bounds_{i}"
50
- # )
51
- # trim_bounds = tuple([round(b, 2) for b in trim_bounds])
52
- # print(trim_bounds)
53
- # trim_settings.append(trim_bounds)
54
-
55
- trim_start = st.slider(f"Trim start for {clip}", 0.0, float(length), 0.00, 0.01, key=f"start_{i}")
56
- trim_end = st.slider(f"Trim end for {clip}", 0.0, float(length), 0.00, 0.01, key=f"end_{i}")
57
- trim_settings.append((trim_start, trim_end))
58
- print(trim_settings)
59
 
60
  with col2:
61
  with st.container(border=True):
62
  st.subheader("Output")
63
- output_file = "output.mp4"
64
  if submit:
65
- pass
66
- # Create the video with individual clip settings
67
- create_advanced_video(number, trim_settings, avatar, output_file)
68
- st.video(output_file, autoplay=True)
 
1
  import streamlit as st
2
+ import requests
3
+ import time
4
+ import os
5
+ from moviepy.editor import VideoFileClip
6
+ from heygen import upload_photo, create_video, get_video_status, download_video, list_talking_photos, split_video, AUDIO_URL
7
  from utils import *
8
 
9
+
10
+ # Streamlit App Configuration
11
  st.set_page_config(
12
  page_title="Number to Video",
13
  page_icon="🎥",
14
+ initial_sidebar_state="expanded",
15
  )
16
 
17
+ # Session State Initialization
18
+ if "talking_photo_id" not in st.session_state:
19
+ st.subheader("Photo Requirements")
20
+ st.write("""
21
+ - The face should be intact and clearly visible.
22
+ - Preferably use real human faces.
23
+ - Ensure only one face is visible in the photo.
24
+ - Face resolution should be larger than 200x200 pixels.
25
+ - The photo should be in JPEG or PNG format.
26
+ """)
27
+ st.image("sample.jpg", use_container_width=True)
28
+ st.session_state.talking_photo_id = None
29
+
30
+ if "video_id" not in st.session_state:
31
+ st.session_state.video_id = None
32
+
33
+ if "video_url" not in st.session_state:
34
+ st.session_state.video_url = None
35
+
36
+ # Sidebar
37
+ st.sidebar.title("Settings")
38
+ uploaded_photo = st.sidebar.file_uploader("Upload a photo", type=["jpeg", "png"])
39
+ upload = st.sidebar.button("Upload Photo", use_container_width=True)
40
+
41
+ if uploaded_photo and upload:
42
+ file_path = f"uploaded_photo.{uploaded_photo.type.split('/')[1]}"
43
+ with open(file_path, "wb") as f:
44
+ f.write(uploaded_photo.read())
45
+
46
+ file_type = "image/jpeg" if uploaded_photo.type in ["image/jpg", "image/jpeg"] else "image/png"
47
+ st.session_state.talking_photo_id = upload_photo(file_path, file_type)
48
+ st.info("Photo uploaded successfully.")
49
+ st.info("Now generate the video.")
50
+
51
+ # Video Creation
52
+ if st.session_state.talking_photo_id:
53
+ create_video_button = st.sidebar.button("Generate Video", use_container_width=True, key="generate_video")
54
+ if create_video_button:
55
+ title="Number to Video"
56
+ st.session_state.video_id = create_video(title, st.session_state.talking_photo_id, AUDIO_URL)
57
+ # st.sidebar.write(f"Video ID: {st.session_state.video_id}")
58
+
59
+ # Video Status Check
60
+ if st.session_state.video_id:
61
+ with st.spinner("Generating video... This may take 2-5 minutes."):
62
+ while True:
63
+ video_status = get_video_status(st.session_state.video_id)
64
+ st.sidebar.info(f"Video Status: {video_status['status']}")
65
+
66
+ if video_status["status"] == "completed":
67
+ st.session_state.video_url = video_status["video_url"]
68
+ st.sidebar.info("Video generation completed.")
69
+ break
70
+ elif video_status["status"] == "failed":
71
+ st.sidebar.warning("Video generation failed. Please try again.")
72
+ break
73
+
74
+ time.sleep(20)
75
+
76
+ # Video Download and Display
77
+ if st.session_state.video_url:
78
+ video_id = st.session_state.video_id
79
+ video_url = st.session_state.video_url
80
+ if not os.path.exists(f"{video_id}.mp4"):
81
+ download_video(video_id, video_url)
82
+ split_video(video_id)
83
+ video_path = f"{video_id}.mp4"
84
+ # download_video(video_id, video_url)
85
+ # split_video(video_id)
86
 
87
+ # Input number
88
+ number = st.text_input("Enter a number between 1 and 99,99,999", "24")
89
+ number = int(number)
90
  col1, col2 = st.columns(2)
91
 
92
  with col1:
93
  with st.container(border=True):
94
+ # st.subheader("Photo avatar")
95
+ # st.divider()
96
 
97
+ # st.image("uploaded_photo.jpg", use_container_width=True)
 
98
 
99
  # Generate clip sequence based on the number
100
  clips = generate_clip_sequence(number)
101
  if clips:
102
  st.write("Generated Clips Sequence:")
103
+ st.divider()
104
  for i, clip in enumerate(clips):
105
  st.write(f"Clip {i + 1}: {clip}")
106
+ submit = st.button("Create Video", use_container_width=True, key="create_video")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
  with col2:
109
  with st.container(border=True):
110
  st.subheader("Output")
111
+ output_file = f"{video_id}_{number}_output.mp4"
112
  if submit:
113
+ combine_video(number, video_id, output_file)
114
+ st.video(output_file, autoplay=True)
 
 
app_v2.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from utils import *
3
+
4
+ st.set_page_config(
5
+ page_title="Number to Video",
6
+ page_icon="🎥",
7
+ initial_sidebar_state="collapsed",
8
+ )
9
+
10
+ # Input number
11
+ number = st.text_input("Enter a number between 1 and 99,99,999", "24")
12
+ number = int(number)
13
+
14
+ if number:
15
+ col1, col2 = st.columns(2)
16
+
17
+ with col1:
18
+ with st.container(border=True):
19
+ st.subheader("Settings")
20
+ st.divider()
21
+
22
+ avatar = st.radio("Choose an avatar", ("Custom avatar", "Template avatar"))
23
+ avatar = "custom" if avatar == "Custom avatar" else "template"
24
+
25
+ # Generate clip sequence based on the number
26
+ clips = generate_clip_sequence(number)
27
+ if clips:
28
+ st.write("Generated Clips Sequence:")
29
+ for i, clip in enumerate(clips):
30
+ st.write(f"Clip {i + 1}: {clip}")
31
+ submit = st.button("Create Video", use_container_width=True)
32
+
33
+ if clips:
34
+ with st.sidebar:
35
+ st.title("Advanced Settings")
36
+ trim_settings = []
37
+ for i, clip in enumerate(clips):
38
+ with st.container(border=True):
39
+ st.write(f"Adjust settings for {clip}:")
40
+ length = get_clip_duration(clip, avatar)
41
+ default_trim_start = 0.10
42
+ default_trim_end = length - 0.10
43
+
44
+ # trim_bounds = st.slider(
45
+ # f"Trim bounds for {clip}",
46
+ # 0.0, length,
47
+ # (default_trim_start, default_trim_end),
48
+ # 0.01,
49
+ # key=f"bounds_{i}"
50
+ # )
51
+ # trim_bounds = tuple([round(b, 2) for b in trim_bounds])
52
+ # print(trim_bounds)
53
+ # trim_settings.append(trim_bounds)
54
+
55
+ trim_start = st.slider(f"Trim start for {clip}", 0.0, float(length), 0.00, 0.01, key=f"start_{i}")
56
+ trim_end = st.slider(f"Trim end for {clip}", 0.0, float(length), 0.00, 0.01, key=f"end_{i}")
57
+ trim_settings.append((trim_start, trim_end))
58
+ print(trim_settings)
59
+
60
+ with col2:
61
+ with st.container(border=True):
62
+ st.subheader("Output")
63
+ output_file = "output.mp4"
64
+ if submit:
65
+ pass
66
+ # Create the video with individual clip settings
67
+ create_advanced_video(number, trim_settings, avatar, output_file)
68
+ st.video(output_file, autoplay=True)
clips_metadata.json CHANGED
@@ -2,194 +2,194 @@
2
  {
3
  "clip_number": 0,
4
  "start_time_ms": 0,
5
- "end_time_ms": 376,
6
  "initial_length": "316",
7
  "file_name": "1_cut.mp4"
8
  },
9
  {
10
  "clip_number": 1,
11
- "start_time_ms": 636,
12
- "end_time_ms": 888,
13
  "file_name": "2_cut.mp4"
14
  },
15
  {
16
  "clip_number": 2,
17
- "start_time_ms": 1179,
18
- "end_time_ms": 1451,
19
  "file_name": "3_cut.mp4"
20
  },
21
  {
22
  "clip_number": 3,
23
- "start_time_ms": 1762,
24
- "end_time_ms": 2037,
25
  "file_name": "4_cut.mp4"
26
  },
27
  {
28
  "clip_number": 4,
29
- "start_time_ms": 2373,
30
- "end_time_ms": 2721,
31
  "file_name": "5_cut.mp4"
32
  },
33
  {
34
  "clip_number": 5,
35
- "start_time_ms": 3007,
36
- "end_time_ms": 3233,
37
  "file_name": "6_cut.mp4"
38
  },
39
  {
40
  "clip_number": 7,
41
- "start_time_ms": 3743,
42
- "end_time_ms": 4163,
43
  "file_name": "7_cut.mp4"
44
  },
45
  {
46
  "clip_number": 8,
47
- "start_time_ms": 4390,
48
- "end_time_ms": 4638,
49
  "file_name": "8_cut.mp4"
50
  },
51
  {
52
  "clip_number": 9,
53
- "start_time_ms": 4956,
54
- "end_time_ms": 5454,
55
  "file_name": "9_cut.mp4"
56
  },
57
  {
58
  "clip_number": 10,
59
- "start_time_ms": 5712,
60
- "end_time_ms": 6036,
61
  "file_name": "10_cut.mp4"
62
  },
63
  {
64
  "clip_number": 11,
65
- "start_time_ms": 6309,
66
- "end_time_ms": 6725,
67
  "file_name": "20_cut.mp4"
68
  },
69
  {
70
  "clip_number": 12,
71
- "start_time_ms": 7020,
72
- "end_time_ms": 7412,
73
  "file_name": "30_cut.mp4"
74
  },
75
  {
76
  "clip_number": 13,
77
- "start_time_ms": 7749,
78
- "end_time_ms": 8116,
79
  "file_name": "40_cut.mp4"
80
  },
81
  {
82
  "clip_number": 14,
83
- "start_time_ms": 8443,
84
- "end_time_ms": 8823,
85
  "file_name": "50_cut.mp4"
86
  },
87
  {
88
  "clip_number": 15,
89
- "start_time_ms": 9107,
90
- "end_time_ms": 9592,
91
  "file_name": "60_cut.mp4"
92
  },
93
  {
94
  "clip_number": 16,
95
- "start_time_ms": 9891,
96
- "end_time_ms": 10371,
97
  "file_name": "70_cut.mp4"
98
  },
99
  {
100
  "clip_number": 17,
101
- "start_time_ms": 10607,
102
- "end_time_ms": 10960,
103
  "file_name": "80_cut.mp4"
104
  },
105
  {
106
  "clip_number": 18,
107
- "start_time_ms": 11182,
108
- "end_time_ms": 11708,
109
  "file_name": "90_cut.mp4"
110
  },
111
  {
112
  "clip_number": 19,
113
- "start_time_ms": 11938,
114
- "end_time_ms": 12434,
115
  "file_name": "11_cut.mp4"
116
  },
117
  {
118
  "clip_number": 20,
119
- "start_time_ms": 12722,
120
- "end_time_ms": 13089,
121
  "file_name": "12_cut.mp4"
122
  },
123
  {
124
  "clip_number": 21,
125
- "start_time_ms": 13394,
126
- "end_time_ms": 13865,
127
  "file_name": "13_cut.mp4"
128
  },
129
  {
130
  "clip_number": 22,
131
- "start_time_ms": 14183,
132
- "end_time_ms": 14621,
133
  "file_name": "14_cut.mp4"
134
  },
135
  {
136
  "clip_number": 23,
137
- "start_time_ms": 14939,
138
- "end_time_ms": 15411,
139
  "file_name": "15_cut.mp4"
140
  },
141
  {
142
  "clip_number": 24,
143
- "start_time_ms": 15684,
144
- "end_time_ms": 16280,
145
  "file_name": "16_cut.mp4"
146
  },
147
  {
148
  "clip_number": 25,
149
- "start_time_ms": 16572,
150
- "end_time_ms": 17153,
151
  "file_name": "17_cut.mp4"
152
  },
153
  {
154
  "clip_number": 26,
155
- "start_time_ms": 17383,
156
- "end_time_ms": 17827,
157
  "file_name": "18_cut.mp4"
158
  },
159
  {
160
  "clip_number": 27,
161
- "start_time_ms": 18028,
162
- "end_time_ms": 18624,
163
  "file_name": "19_cut.mp4"
164
  },
165
  {
166
  "clip_number": 28,
167
- "start_time_ms": 18910,
168
- "end_time_ms": 19301,
169
  "file_name": "100_cut.mp4"
170
  },
171
  {
172
  "clip_number": 29,
173
- "start_time_ms": 19578,
174
- "end_time_ms": 20046,
175
  "file_name": "1000_cut.mp4"
176
  },
177
  {
178
  "clip_number": 30,
179
- "start_time_ms": 20268,
180
- "end_time_ms": 20626,
181
  "file_name": "100000_cut.mp4"
182
  },
183
  {
184
  "clip_number": 31,
185
- "start_time_ms": 21018,
186
- "end_time_ms": 22227,
187
  "file_name": "Intro_cut.mp4"
188
  },
189
  {
190
  "clip_number": 32,
191
- "start_time_ms": 22692,
192
- "end_time_ms": 23218,
193
  "file_name": "Conclusion_cut.mp4"
194
  }
195
  ]
 
2
  {
3
  "clip_number": 0,
4
  "start_time_ms": 0,
5
+ "end_time_ms": 356,
6
  "initial_length": "316",
7
  "file_name": "1_cut.mp4"
8
  },
9
  {
10
  "clip_number": 1,
11
+ "start_time_ms": 656,
12
+ "end_time_ms": 868,
13
  "file_name": "2_cut.mp4"
14
  },
15
  {
16
  "clip_number": 2,
17
+ "start_time_ms": 1199,
18
+ "end_time_ms": 1431,
19
  "file_name": "3_cut.mp4"
20
  },
21
  {
22
  "clip_number": 3,
23
+ "start_time_ms": 1782,
24
+ "end_time_ms": 2017,
25
  "file_name": "4_cut.mp4"
26
  },
27
  {
28
  "clip_number": 4,
29
+ "start_time_ms": 2393,
30
+ "end_time_ms": 2701,
31
  "file_name": "5_cut.mp4"
32
  },
33
  {
34
  "clip_number": 5,
35
+ "start_time_ms": 3027,
36
+ "end_time_ms": 3213,
37
  "file_name": "6_cut.mp4"
38
  },
39
  {
40
  "clip_number": 7,
41
+ "start_time_ms": 3763,
42
+ "end_time_ms": 4143,
43
  "file_name": "7_cut.mp4"
44
  },
45
  {
46
  "clip_number": 8,
47
+ "start_time_ms": 4410,
48
+ "end_time_ms": 4618,
49
  "file_name": "8_cut.mp4"
50
  },
51
  {
52
  "clip_number": 9,
53
+ "start_time_ms": 4976,
54
+ "end_time_ms": 5434,
55
  "file_name": "9_cut.mp4"
56
  },
57
  {
58
  "clip_number": 10,
59
+ "start_time_ms": 5732,
60
+ "end_time_ms": 6016,
61
  "file_name": "10_cut.mp4"
62
  },
63
  {
64
  "clip_number": 11,
65
+ "start_time_ms": 6329,
66
+ "end_time_ms": 6705,
67
  "file_name": "20_cut.mp4"
68
  },
69
  {
70
  "clip_number": 12,
71
+ "start_time_ms": 7040,
72
+ "end_time_ms": 7392,
73
  "file_name": "30_cut.mp4"
74
  },
75
  {
76
  "clip_number": 13,
77
+ "start_time_ms": 7769,
78
+ "end_time_ms": 8096,
79
  "file_name": "40_cut.mp4"
80
  },
81
  {
82
  "clip_number": 14,
83
+ "start_time_ms": 8463,
84
+ "end_time_ms": 8803,
85
  "file_name": "50_cut.mp4"
86
  },
87
  {
88
  "clip_number": 15,
89
+ "start_time_ms": 9127,
90
+ "end_time_ms": 9572,
91
  "file_name": "60_cut.mp4"
92
  },
93
  {
94
  "clip_number": 16,
95
+ "start_time_ms": 9911,
96
+ "end_time_ms": 10351,
97
  "file_name": "70_cut.mp4"
98
  },
99
  {
100
  "clip_number": 17,
101
+ "start_time_ms": 10627,
102
+ "end_time_ms": 10940,
103
  "file_name": "80_cut.mp4"
104
  },
105
  {
106
  "clip_number": 18,
107
+ "start_time_ms": 11202,
108
+ "end_time_ms": 11688,
109
  "file_name": "90_cut.mp4"
110
  },
111
  {
112
  "clip_number": 19,
113
+ "start_time_ms": 11958,
114
+ "end_time_ms": 12414,
115
  "file_name": "11_cut.mp4"
116
  },
117
  {
118
  "clip_number": 20,
119
+ "start_time_ms": 12742,
120
+ "end_time_ms": 13069,
121
  "file_name": "12_cut.mp4"
122
  },
123
  {
124
  "clip_number": 21,
125
+ "start_time_ms": 13414,
126
+ "end_time_ms": 13845,
127
  "file_name": "13_cut.mp4"
128
  },
129
  {
130
  "clip_number": 22,
131
+ "start_time_ms": 14203,
132
+ "end_time_ms": 14601,
133
  "file_name": "14_cut.mp4"
134
  },
135
  {
136
  "clip_number": 23,
137
+ "start_time_ms": 14959,
138
+ "end_time_ms": 15391,
139
  "file_name": "15_cut.mp4"
140
  },
141
  {
142
  "clip_number": 24,
143
+ "start_time_ms": 15704,
144
+ "end_time_ms": 16260,
145
  "file_name": "16_cut.mp4"
146
  },
147
  {
148
  "clip_number": 25,
149
+ "start_time_ms": 16592,
150
+ "end_time_ms": 17133,
151
  "file_name": "17_cut.mp4"
152
  },
153
  {
154
  "clip_number": 26,
155
+ "start_time_ms": 17403,
156
+ "end_time_ms": 17807,
157
  "file_name": "18_cut.mp4"
158
  },
159
  {
160
  "clip_number": 27,
161
+ "start_time_ms": 18048,
162
+ "end_time_ms": 18604,
163
  "file_name": "19_cut.mp4"
164
  },
165
  {
166
  "clip_number": 28,
167
+ "start_time_ms": 18930,
168
+ "end_time_ms": 19281,
169
  "file_name": "100_cut.mp4"
170
  },
171
  {
172
  "clip_number": 29,
173
+ "start_time_ms": 19598,
174
+ "end_time_ms": 20026,
175
  "file_name": "1000_cut.mp4"
176
  },
177
  {
178
  "clip_number": 30,
179
+ "start_time_ms": 20288,
180
+ "end_time_ms": 20606,
181
  "file_name": "100000_cut.mp4"
182
  },
183
  {
184
  "clip_number": 31,
185
+ "start_time_ms": 21038,
186
+ "end_time_ms": 22207,
187
  "file_name": "Intro_cut.mp4"
188
  },
189
  {
190
  "clip_number": 32,
191
+ "start_time_ms": 22712,
192
+ "end_time_ms": 23198,
193
  "file_name": "Conclusion_cut.mp4"
194
  }
195
  ]
dev.ipynb CHANGED
@@ -208,7 +208,7 @@
208
  },
209
  {
210
  "cell_type": "code",
211
- "execution_count": 16,
212
  "metadata": {},
213
  "outputs": [],
214
  "source": [
@@ -224,7 +224,7 @@
224
  " {\n",
225
  " \"character\": {\n",
226
  " \"type\": \"avatar\",\n",
227
- " \"avatar_id\": \"f5db9448825249168273a87cd98f439d\",\n",
228
  " \"scale\": 1.0,\n",
229
  " \"avatar_style\": \"normal\",\n",
230
  " \"offset\": {\n",
@@ -245,14 +245,49 @@
245
  },
246
  {
247
  "cell_type": "code",
248
- "execution_count": 17,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  "metadata": {},
250
  "outputs": [
251
  {
252
  "name": "stdout",
253
  "output_type": "stream",
254
  "text": [
255
- "{\"error\": null, \"data\": {\"video_id\": \"53cf5d6942454560971ba70926bdacc5\"}}\n"
256
  ]
257
  }
258
  ],
@@ -277,7 +312,28 @@
277
  },
278
  {
279
  "cell_type": "code",
280
- "execution_count": 20,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  "metadata": {},
282
  "outputs": [],
283
  "source": [
@@ -289,33 +345,33 @@
289
  "}\n",
290
  "\n",
291
  "\n",
292
- "response = requests.get(url, headers=headers, params={\"video_id\": \"53cf5d6942454560971ba70926bdacc5\"}).json()"
293
  ]
294
  },
295
  {
296
  "cell_type": "code",
297
- "execution_count": 21,
298
  "metadata": {},
299
  "outputs": [
300
  {
301
  "data": {
302
  "text/plain": [
303
  "{'code': 100,\n",
304
- " 'data': {'callback_id': 'your_custom_id_123',\n",
305
- " 'caption_url': '',\n",
306
- " 'created_at': 1736225867,\n",
307
- " 'duration': 23.458,\n",
308
  " 'error': None,\n",
309
- " 'gif_url': 'https://resource2.heygen.ai/video/gifs/53cf5d6942454560971ba70926bdacc5.gif',\n",
310
- " 'id': '53cf5d6942454560971ba70926bdacc5',\n",
311
- " 'status': 'completed',\n",
312
- " 'thumbnail_url': 'https://files2.heygen.ai/aws_pacific/avatar_tmp/a0cc99225ed34eb9811246368d94768a/53cf5d6942454560971ba70926bdacc5.jpeg?Expires=1736830809&Signature=kFj3rXlTETYCBXvi-8A37~-aVx1sZCBsDCzdU6W-mU0A45lsnGd1ao9v7QmkR5CqRkIgKNg9~zNKOmARVIHt9p37zpRN9uzcP3XUQ0wBS7C61pYZU-6KXxlfDbVt-aiM2BmvfhKZHA1W7p~fBgwYxGNt25LluA7hFLpczGswMVLEWb7z0JV84UfaZmU~5IqkCt-Fp-o69YlSZ26NvEjm~X2WpM2ILZ9bRpjrNkNNQ3LvUaO-~9XRhEEhe8ANeZgG1Jl7oh6dsQgLTC3TKwrFjsGMeXS2L3etIAja01iWdpEvbrW1oeWXSdPo8AW-0Zac-RJ4ZFymk8kJY6RiWDNxEQ__&Key-Pair-Id=K38HBHX5LX3X2H',\n",
313
- " 'video_url': 'https://files2.heygen.ai/aws_pacific/avatar_tmp/a0cc99225ed34eb9811246368d94768a/53cf5d6942454560971ba70926bdacc5.mp4?Expires=1736830768&Signature=hub7aBI-57uvpQR-kL6eltYrFiacZxEOZKSKE60ntls~f2k87qacQNIiCbkXicKN3Q9CMyG0Io4vqMAvDapSrQXcaOfM6mOyBIV718LjCGXh9r35VVaMvbSl1onIIuoHeW2jYfeBVZ-mUgDHG~3ciHodXKX~VlebM595bHsg65LoBxYNVYEMJIHShxRdwE8xSmWahBUPubf~pegkCtJA1MBU0a7oN3jkey7EMlTDiBvRvXdES332mZkoNpFc7Ecfj6Lv-5k7T~ZBXq69bN79pnn4RVaw7vvsJiQnrKlrKYVe90HWb0MIVtjRkLX2cloWFhHpLh3picgprT0ixUhjsg__&Key-Pair-Id=K38HBHX5LX3X2H',\n",
314
  " 'video_url_caption': None},\n",
315
  " 'message': 'Success'}"
316
  ]
317
  },
318
- "execution_count": 21,
319
  "metadata": {},
320
  "output_type": "execute_result"
321
  }
@@ -414,7 +470,7 @@
414
  },
415
  {
416
  "cell_type": "code",
417
- "execution_count": 27,
418
  "metadata": {},
419
  "outputs": [],
420
  "source": [
@@ -423,13 +479,81 @@
423
  " clips_metadata = json.load(f)\n",
424
  "\n",
425
  "for item in clips_metadata:\n",
426
- " item[\"start_time_ms\"]=item[\"start_time_ms\"]+40\n",
427
- " item[\"end_time_ms\"]=item[\"end_time_ms\"]-40\n",
428
  "\n",
429
  "# update the file with new timestamps\n",
430
  "with open(\"clips_metadata.json\", \"w\") as f:\n",
431
  " json.dump(clips_metadata, f, indent=4)"
432
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  }
434
  ],
435
  "metadata": {
 
208
  },
209
  {
210
  "cell_type": "code",
211
+ "execution_count": 49,
212
  "metadata": {},
213
  "outputs": [],
214
  "source": [
 
224
  " {\n",
225
  " \"character\": {\n",
226
  " \"type\": \"avatar\",\n",
227
+ " \"avatar_id\": \"42b4f9358cc9443a8610fda90bd3ad87\",\n",
228
  " \"scale\": 1.0,\n",
229
  " \"avatar_style\": \"normal\",\n",
230
  " \"offset\": {\n",
 
245
  },
246
  {
247
  "cell_type": "code",
248
+ "execution_count": 53,
249
+ "metadata": {},
250
+ "outputs": [],
251
+ "source": [
252
+ "payload = {\n",
253
+ " \"title\": \"Sample Talking Photo Video\",\n",
254
+ " \"caption\": False,\n",
255
+ " \"callback_id\": \"your_custom_id_123\",\n",
256
+ " \"dimension\": {\n",
257
+ " \"width\": 720,\n",
258
+ " \"height\": 1280\n",
259
+ " },\n",
260
+ " \"video_inputs\": [\n",
261
+ " {\n",
262
+ " \"character\": {\n",
263
+ " \"type\": \"talking_photo\",\n",
264
+ " \"talking_photo_id\": 'cf557bdc461b487fb703b0c3138df325'\n",
265
+ " },\n",
266
+ " \"voice\": {\n",
267
+ " \"type\": \"audio\",\n",
268
+ " \"audio_url\": \"https://drive.google.com/file/d/1CF8TjWwPWiIPJX3D1bjpMopinHt4iw1D/view?usp=sharing\"\n",
269
+ " },\n",
270
+ " \"talking_style\": \"stable\",\n",
271
+ " \"expression\": \"happy\",\n",
272
+ " # \"background\": {\n",
273
+ " # \"type\": \"color\",\n",
274
+ " # \"value\": \"#FAFAFA\"\n",
275
+ " # }\n",
276
+ " }\n",
277
+ " ]\n",
278
+ "}\n"
279
+ ]
280
+ },
281
+ {
282
+ "cell_type": "code",
283
+ "execution_count": 54,
284
  "metadata": {},
285
  "outputs": [
286
  {
287
  "name": "stdout",
288
  "output_type": "stream",
289
  "text": [
290
+ "{\"error\": null, \"data\": {\"video_id\": \"d8194dfd76fa40c49bfc9a99c67ffbe3\"}}\n"
291
  ]
292
  }
293
  ],
 
312
  },
313
  {
314
  "cell_type": "code",
315
+ "execution_count": 56,
316
+ "metadata": {},
317
+ "outputs": [
318
+ {
319
+ "data": {
320
+ "text/plain": [
321
+ "'d8194dfd76fa40c49bfc9a99c67ffbe3'"
322
+ ]
323
+ },
324
+ "execution_count": 56,
325
+ "metadata": {},
326
+ "output_type": "execute_result"
327
+ }
328
+ ],
329
+ "source": [
330
+ "video_id = response.json()[\"data\"][\"video_id\"]\n",
331
+ "video_id"
332
+ ]
333
+ },
334
+ {
335
+ "cell_type": "code",
336
+ "execution_count": 57,
337
  "metadata": {},
338
  "outputs": [],
339
  "source": [
 
345
  "}\n",
346
  "\n",
347
  "\n",
348
+ "response = requests.get(url, headers=headers, params={\"video_id\": video_id}).json()"
349
  ]
350
  },
351
  {
352
  "cell_type": "code",
353
+ "execution_count": 58,
354
  "metadata": {},
355
  "outputs": [
356
  {
357
  "data": {
358
  "text/plain": [
359
  "{'code': 100,\n",
360
+ " 'data': {'callback_id': None,\n",
361
+ " 'caption_url': None,\n",
362
+ " 'created_at': 1736231063,\n",
363
+ " 'duration': None,\n",
364
  " 'error': None,\n",
365
+ " 'gif_url': None,\n",
366
+ " 'id': 'd8194dfd76fa40c49bfc9a99c67ffbe3',\n",
367
+ " 'status': 'processing',\n",
368
+ " 'thumbnail_url': None,\n",
369
+ " 'video_url': None,\n",
370
  " 'video_url_caption': None},\n",
371
  " 'message': 'Success'}"
372
  ]
373
  },
374
+ "execution_count": 58,
375
  "metadata": {},
376
  "output_type": "execute_result"
377
  }
 
470
  },
471
  {
472
  "cell_type": "code",
473
+ "execution_count": 31,
474
  "metadata": {},
475
  "outputs": [],
476
  "source": [
 
479
  " clips_metadata = json.load(f)\n",
480
  "\n",
481
  "for item in clips_metadata:\n",
482
+ " item[\"start_time_ms\"]=item[\"start_time_ms\"]+20\n",
483
+ " item[\"end_time_ms\"]=item[\"end_time_ms\"]-20\n",
484
  "\n",
485
  "# update the file with new timestamps\n",
486
  "with open(\"clips_metadata.json\", \"w\") as f:\n",
487
  " json.dump(clips_metadata, f, indent=4)"
488
  ]
489
+ },
490
+ {
491
+ "cell_type": "code",
492
+ "execution_count": 45,
493
+ "metadata": {},
494
+ "outputs": [
495
+ {
496
+ "name": "stdout",
497
+ "output_type": "stream",
498
+ "text": [
499
+ "{'code': 100, 'data': {'talking_photo_id': 'cf557bdc461b487fb703b0c3138df325', 'talking_photo_url': 'https://files2.heygen.ai/prod/movio/url_upload/user_upload/a0cc99225ed34eb9811246368d94768a/f670829259c24a0197966e9f7c767ada.image/png?Expires=1736835408&Signature=EcHmA-5R6USvUeus0Ku~NCoCfg~T9NteKjfR59HEYOOurb7in9T4QB3Um8aeHBGLhIz1~5epJuvzIVnuB9H2Ab5uwV9B84KdXWK5rWhrCefRRn8JQxFOnvoCDtQFeHhuTs8957xbboJkFdLiv8OLli90lkmO0fzZecs7WB~WFFhQGqeAhCPjAvQt325ECSngNA2Z4MbI96YbOanE5GiRvUg56hczdb9XqhZktDkZKIDZGl3jk45BcmoaYh6N~sySnGN4kBe-Ny8exFzH0cjloX3gwkHnwsitmI2yQKmse7LKQFEt8WyQYxQ6z6q4YN1FpjKqMjK1T~~UGLepdGIDCA__&Key-Pair-Id=K38HBHX5LX3X2H'}, 'msg': None, 'message': None}\n"
500
+ ]
501
+ }
502
+ ],
503
+ "source": [
504
+ "api_key = \"YTBjYzk5MjI1ZWQzNGViOTgxMTI0NjM2OGQ5NDc2OGEtMTczMzk5MjA5MA==\"\n",
505
+ "\n",
506
+ "with open(\"man_portrait.png\", \"rb\") as f:\n",
507
+ " resp = requests.post(\"https://upload.heygen.com/v1/talking_photo\", data=f, headers={\"Content-Type\": \"image/png\", \"x-api-key\": api_key})\n",
508
+ " print(resp.json())"
509
+ ]
510
+ },
511
+ {
512
+ "cell_type": "code",
513
+ "execution_count": 52,
514
+ "metadata": {},
515
+ "outputs": [
516
+ {
517
+ "data": {
518
+ "text/plain": [
519
+ "'cf557bdc461b487fb703b0c3138df325'"
520
+ ]
521
+ },
522
+ "execution_count": 52,
523
+ "metadata": {},
524
+ "output_type": "execute_result"
525
+ }
526
+ ],
527
+ "source": [
528
+ "response = resp.json()\n",
529
+ "response[\"data\"][\"talking_photo_id\"]"
530
+ ]
531
+ },
532
+ {
533
+ "cell_type": "code",
534
+ "execution_count": null,
535
+ "metadata": {},
536
+ "outputs": [],
537
+ "source": [
538
+ "\n",
539
+ "url = \"https://api.heygen.com/v1/talking_photo.list\"\n",
540
+ "\n",
541
+ "headers = {\n",
542
+ " \"accept\": \"application/json\",\n",
543
+ " \"x-api-key\": api_key\n",
544
+ "}\n",
545
+ "\n",
546
+ "response = requests.get(url, headers=headers)\n",
547
+ "\n",
548
+ "print(response.text)"
549
+ ]
550
+ },
551
+ {
552
+ "cell_type": "code",
553
+ "execution_count": null,
554
+ "metadata": {},
555
+ "outputs": [],
556
+ "source": []
557
  }
558
  ],
559
  "metadata": {
heygen.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+ from moviepy.editor import VideoFileClip
4
+ import os
5
+
6
+ API_KEY = "YTBjYzk5MjI1ZWQzNGViOTgxMTI0NjM2OGQ5NDc2OGEtMTczMzk5MjA5MA=="
7
+ AUDIO_URL = "https://drive.google.com/file/d/1CF8TjWwPWiIPJX3D1bjpMopinHt4iw1D/view?usp=sharing"
8
+
9
+
10
+ # API ENDPOINTS
11
+ #GET
12
+ VIDEO_GENERATION_STATUS = "https://api.heygen.com/v1/video_status.get"
13
+ LIST_TALKING_PHOTOS = "https://api.heygen.com/v1/talking_photo.list"
14
+
15
+ #POST
16
+ VIDEO_GENERATION = "https://api.heygen.com/v2/video/generate"
17
+ UPLOAD_PHOTO = "https://upload.heygen.com/v1/talking_photo"
18
+
19
+
20
+ def video_generation_payload(title, avatar_id, audio_url):
21
+ return {
22
+ "title": title,
23
+ "caption": False,
24
+ # "callback_id": "callback_id",
25
+ "dimension": {
26
+ "width": 720,
27
+ "height": 1280
28
+ },
29
+ "video_inputs": [
30
+ {
31
+ "character": {
32
+ "type": "avatar",
33
+ "avatar_id": avatar_id,
34
+ "scale": 1.0,
35
+ "avatar_style": "normal",
36
+ "offset": {
37
+ "x": 0.0,
38
+ "y": 0.0
39
+ },
40
+ "matting": False
41
+ },
42
+ "voice": {
43
+ "type": "audio",
44
+ "audio_url": audio_url
45
+ },
46
+ }
47
+ ],
48
+ # "callback_url": "callback_url"
49
+ }
50
+
51
+
52
+ def talking_photo_payload(title, talking_photo_id, audio_url):
53
+ print(f"Creating video with title: {title}, talking_photo_id: {talking_photo_id}, audio_url: {audio_url}")
54
+ return {
55
+ "title": title,
56
+ "caption": False,
57
+ # "callback_id": "callback_id",
58
+ "dimension": {
59
+ "width": 720,
60
+ "height": 1280
61
+ },
62
+ "video_inputs": [
63
+ {
64
+ "character": {
65
+ "type": "talking_photo",
66
+ "talking_photo_id": talking_photo_id
67
+ },
68
+ "voice": {
69
+ "type": "audio",
70
+ "audio_url": audio_url
71
+ },
72
+ "talking_style": "stable",
73
+ "expression": "happy",
74
+ }
75
+ ],
76
+ # "callback_url": "callback_url"
77
+ }
78
+
79
+
80
+ def upload_photo(file_path, type):
81
+ print(f"Uploading photo {file_path}")
82
+ with open(file_path, "rb") as f:
83
+ resp = requests.post(UPLOAD_PHOTO, data=f, headers={"Content-Type": type, "x-api-key": API_KEY}).json()
84
+ try:
85
+ talking_photo_id = resp["data"]["talking_photo_id"]
86
+ return talking_photo_id
87
+ except Exception as e:
88
+ print(f"Error uploading photo: {e}")
89
+ return resp
90
+
91
+ def list_talking_photos():
92
+ print("Listing talking photos")
93
+ headers = {
94
+ "accept": "application/json",
95
+ "x-api-key": API_KEY
96
+ }
97
+ response = requests.get(LIST_TALKING_PHOTOS, headers=headers).json()
98
+ return response
99
+
100
+
101
+ def download_video(video_id, video_url):
102
+ download_video = requests.get(video_url)
103
+ print(f"Downloading video {video_id}")
104
+ with open(f"{video_id}.mp4", "wb") as f:
105
+ f.write(download_video.content)
106
+
107
+
108
+ def create_video(title, avatar_id, audio_url):
109
+ print(f"Creating video with title: {title}, avatar_id: {avatar_id}, audio_url: {audio_url}")
110
+ headers = {
111
+ "accept": "application/json",
112
+ "content-type": "application/json",
113
+ "x-api-key": API_KEY
114
+ }
115
+ payload = talking_photo_payload(title, avatar_id, audio_url)
116
+ response = requests.post(VIDEO_GENERATION, json=payload, headers=headers).json()
117
+
118
+ try:
119
+ video_id = response["data"]["video_id"]
120
+ print(f"Video ID: {video_id}")
121
+ return video_id
122
+ except Exception as e:
123
+ print(f"Error creating video: {e}")
124
+ return response
125
+
126
+
127
+ def get_video_status(video_id):
128
+ headers = {
129
+ "accept": "application/json",
130
+ "x-api-key": API_KEY
131
+ }
132
+ response = requests.get(VIDEO_GENERATION_STATUS, headers=headers, params={"video_id": video_id}).json()
133
+
134
+ print(f"Video status: {response['data']['status']}")
135
+
136
+ return {
137
+ "id": response["data"]["id"],
138
+ "status": response["data"]["status"],
139
+ "video_url": response["data"]["video_url"],
140
+ "thumbnail_url": response["data"]["thumbnail_url"]
141
+ }
142
+
143
+
144
+ def split_video(video_id):
145
+ print(f"Splitting video {video_id}.mp4")
146
+ with open("clips_metadata.json", "r") as f:
147
+ clips_metadata = json.load(f)
148
+
149
+ video = VideoFileClip(f"{video_id}.mp4")
150
+
151
+ clips = []
152
+ for clip_data in clips_metadata:
153
+ start_time = clip_data["start_time_ms"]
154
+ end_time = clip_data["end_time_ms"]
155
+
156
+ # convert milliseconds to seconds
157
+ start_time /= 1000
158
+ end_time /= 1000
159
+
160
+ try:
161
+ clip_name = clip_data["file_name"].split(".")[0]+".mp4"
162
+ clip = video.subclip(start_time, end_time)
163
+ clips.append(clip)
164
+ except Exception as e:
165
+ print(f"Error in clip {clip_name}: {e}")
166
+
167
+ clips_dir = f"{video_id}_clips"
168
+ os.makedirs(clips_dir, exist_ok=True)
169
+ print(f"Saving clips to {clips_dir}")
170
+
171
+ for i, clip in enumerate(clips):
172
+ clip_path = os.path.join(clips_dir, clips_metadata[i]["file_name"].split(".")[0]+".mp4")
173
+ clip.write_videofile(clip_path, codec="libx264", audio_codec="aac")
sample.jpg ADDED
utils.py CHANGED
@@ -80,7 +80,7 @@ def cut_clip(clip, st, en):
80
 
81
 
82
 
83
- def create_video(number,st, en, output_file="output.mp4"):
84
  """
85
  Creates a video for the given number using pre-defined video clips.
86
  """
@@ -146,5 +146,35 @@ def create_advanced_video(number, trim_settings, avatar, output_file="output.mp4
146
  if video_clips:
147
  final_video = concatenate_videoclips(clips_to_combine)
148
  final_video.write_videofile(output_file, codec="libx264", audio_codec="aac")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  else:
150
  print("No clips to combine. Video creation failed.")
 
80
 
81
 
82
 
83
+ def create_video_old(number,st, en, output_file="output.mp4"):
84
  """
85
  Creates a video for the given number using pre-defined video clips.
86
  """
 
146
  if video_clips:
147
  final_video = concatenate_videoclips(clips_to_combine)
148
  final_video.write_videofile(output_file, codec="libx264", audio_codec="aac")
149
+ else:
150
+ print("No clips to combine. Video creation failed.")
151
+
152
+
153
+ def combine_video(number, video_id, output_file="output.mp4"):
154
+ path = f"{video_id}_clips"
155
+ intro_file = f"{path}/Intro_cut.mp4"
156
+ conclusion_file = f"{path}/Conclusion_cut.mp4"
157
+
158
+ clips_to_combine = [intro_file]
159
+
160
+ clips = generate_clip_sequence(number)
161
+ for clip in clips:
162
+ clips_to_combine.append(trimmed_clip_path(clip, avatar_path=path))
163
+
164
+ clips_to_combine.append(conclusion_file)
165
+
166
+ # Load and combine video clips
167
+ video_clips = []
168
+ for clip_path in clips_to_combine:
169
+ try:
170
+ clip = VideoFileClip(clip_path)
171
+ video_clips.append(clip)
172
+ except Exception as e:
173
+ print(f"Error loading clip {clip_path}: {e}")
174
+
175
+ # Concatenate the video clips
176
+ if video_clips:
177
+ final_video = concatenate_videoclips(video_clips)
178
+ final_video.write_videofile(output_file, codec="libx264", audio_codec="aac")
179
  else:
180
  print("No clips to combine. Video creation failed.")