Files changed (6) hide show
  1. .gitignore +3 -2
  2. app.py +131 -98
  3. aws_utils.py +2 -2
  4. core.py +279 -393
  5. parameters.py +5 -3
  6. script_gen.py +22 -15
.gitignore CHANGED
@@ -1,2 +1,3 @@
1
- .env
2
- __pycache__
 
 
1
+ *.env
2
+ __pycache__/
3
+ .gradio/
app.py CHANGED
@@ -11,10 +11,10 @@ with gr.Blocks() as demo:
11
  current_episode = gr.State(-1)
12
  current_scene = gr.State(-1)
13
  current_frame = gr.State(-1)
14
- episodes_data = gr.State({})
15
  character_data = gr.State({})
16
  current_frame_data = gr.State(None)
17
- details = gr.State({})
18
  image_data_b64 = gr.State([])
19
  choice = gr.State([])
20
 
@@ -34,11 +34,11 @@ with gr.Blocks() as demo:
34
  with gr.Column():
35
  load_images = gr.Button("Load Images")
36
  developer = gr.Checkbox(
37
- value=False, label="Enable Developer Mode", visible=False
38
  )
39
 
40
  images = gr.Gallery(
41
- label="Select an Image", elem_id="image_select", columns=4, height=300
42
  )
43
 
44
  with gr.Row():
@@ -58,43 +58,46 @@ with gr.Blocks() as demo:
58
 
59
  with gr.Column(visible=False) as developer_options:
60
  with gr.Column():
61
- setting = gr.Textbox(label="Frame Setting")
62
  with gr.Row():
63
  with gr.Column():
64
  gr.Markdown("## Composition #1")
65
- prompt_1 = gr.TextArea(label="Image Prompt")
66
  seed_1 = gr.Textbox(label="Generation Seed")
67
  with gr.Column():
68
  gr.Markdown("## Composition #2")
69
- prompt_2 = gr.TextArea(label="Image Prompt")
70
  seed_2 = gr.Textbox(label="Generation Seed")
71
  with gr.Row():
72
  with gr.Column():
73
  gr.Markdown("## Composition #3")
74
- prompt_3 = gr.TextArea(label="Image Prompt")
75
  seed_3 = gr.Textbox(label="Generation Seed")
76
  with gr.Column():
77
  gr.Markdown("## Composition #4")
78
- prompt_4 = gr.TextArea(label="Image Prompt")
79
  seed_4 = gr.Textbox(label="Generation Seed")
80
 
81
  with gr.Row():
82
  regenerate_comps_btn = gr.Button(value="Regenerate Compositions")
83
- save_components_btn = gr.Button(value="Save Compositions")
84
 
85
  with gr.Column():
86
  negative_prompt = gr.TextArea(
87
  value="",
88
  label="Negative Prompt",
 
89
  )
90
- # chars = gr.Textbox(value="[]", label="Related Characters")
91
- chars = gr.CheckboxGroup(
92
  choices=[], value=[], label="Related Characters", interactive=True
93
  )
94
 
95
  with gr.Row():
96
- height = gr.Textbox(value="1024", label="Image Height")
97
- width = gr.Textbox(value="1024", label="Image Width")
 
 
 
 
98
  visual_style = gr.Dropdown(
99
  choices=parameters.VISUAL_CHOICES,
100
  label="Current Visual Style",
@@ -106,57 +109,65 @@ with gr.Blocks() as demo:
106
  save_btn = gr.Button("Save")
107
 
108
  ############################################ EVENTS ############################################
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  load_metadata.click(
110
  core.load_metadata_fn,
111
  inputs=[comic_id],
 
 
 
 
112
  outputs=[
113
- episode_dropdown,
114
  frame_dropdown,
115
  current_episode,
116
  current_frame,
117
- episodes_data,
118
- character_data,
119
- details,
120
- developer,
121
  ],
122
  )
123
 
124
  episode_dropdown.input(
125
- core.load_dropdown_fn,
126
- inputs=[episode_dropdown],
127
  outputs=[
128
- episode_dropdown,
129
  frame_dropdown,
130
  current_episode,
131
  current_frame,
 
 
132
  ],
133
  )
134
 
135
  frame_dropdown.input(
136
- core.load_dropdown_fn_v2,
137
  inputs=[frame_dropdown],
138
- outputs=[
139
- current_frame,
140
- ],
141
  )
142
 
143
  load_images.click(
144
- core.load_from_dropdown,
145
  inputs=[
146
- episodes_data,
147
- current_episode,
148
  current_frame,
149
- developer,
150
- character_data,
151
  ],
152
  outputs=[
153
- episode_dropdown,
154
- frame_dropdown,
155
- # Textual data returned from load_text_data()
156
  images,
157
- episodes_data,
158
- current_episode,
159
- current_frame,
160
  image_description,
161
  narration,
162
  character,
@@ -171,33 +182,44 @@ with gr.Blocks() as demo:
171
  seed_3,
172
  prompt_4,
173
  seed_4,
174
- chars,
175
  ],
 
 
 
 
176
  )
177
 
178
  # When an image is clicked
179
  def get_select_index(evt: gr.SelectData, images):
180
  return images[evt.index]
181
-
182
  images.select(get_select_index, images, selected_image)
183
 
184
  next_button.click(
185
  core.load_data_next,
186
  inputs=[
187
- episodes_data,
188
  current_episode,
189
  current_frame,
190
- developer,
191
- character_data,
192
  ],
193
  outputs=[
194
  episode_dropdown,
195
  frame_dropdown,
196
- # Textual data returned from load_text_data()
197
- images,
198
- episodes_data,
199
  current_episode,
200
  current_frame,
 
 
 
 
 
 
 
 
 
 
 
 
201
  image_description,
202
  narration,
203
  character,
@@ -212,27 +234,43 @@ with gr.Blocks() as demo:
212
  seed_3,
213
  prompt_4,
214
  seed_4,
215
- chars,
216
  ],
 
 
 
 
 
 
 
 
217
  )
218
 
219
  prev_button.click(
220
  core.load_data_prev,
221
  inputs=[
222
- episodes_data,
223
  current_episode,
224
  current_frame,
225
- developer,
226
- character_data,
227
  ],
228
  outputs=[
229
  episode_dropdown,
230
  frame_dropdown,
231
- # Textual data returned from load_text_data()
232
- images,
233
- episodes_data,
234
  current_episode,
235
  current_frame,
 
 
 
 
 
 
 
 
 
 
 
 
236
  image_description,
237
  narration,
238
  character,
@@ -247,8 +285,15 @@ with gr.Blocks() as demo:
247
  seed_3,
248
  prompt_4,
249
  seed_4,
250
- chars,
251
  ],
 
 
 
 
 
 
 
 
252
  )
253
 
254
  save_button.click(
@@ -262,23 +307,8 @@ with gr.Blocks() as demo:
262
  outputs=[],
263
  )
264
 
265
- developer.change(
266
- core.toggle_developer_options,
267
- inputs=[developer, prompt_1, prompt_2, prompt_3, prompt_4, setting],
268
- outputs=[developer_options, prompt_1, prompt_2, prompt_3, prompt_4, setting],
269
- ).then(
270
- lambda: (
271
- gr.update(interactive=True),
272
- gr.update(interactive=True),
273
- gr.update(interactive=True),
274
- gr.update(interactive=True),
275
- gr.update(interactive=True),
276
- ),
277
- outputs=[prompt_1, prompt_2, prompt_3, prompt_4, setting],
278
- )
279
-
280
  regenerate_comps_btn.click(
281
- core.regenerate_composition_data,
282
  inputs=[
283
  image_description,
284
  narration,
@@ -286,12 +316,31 @@ with gr.Blocks() as demo:
286
  dialouge,
287
  location,
288
  setting,
289
- chars,
290
  current_episode,
291
  current_frame,
292
- episodes_data,
 
 
293
  ],
294
  outputs=[
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  prompt_1,
296
  seed_1,
297
  prompt_2,
@@ -299,16 +348,17 @@ with gr.Blocks() as demo:
299
  prompt_3,
300
  seed_3,
301
  prompt_4,
302
- seed_4,
303
  ],
 
304
  )
305
 
306
- save_components_btn.click(
307
- core.save_image_compositions,
308
  inputs=[
309
  current_episode,
310
  current_frame,
311
- details,
312
  comic_id,
313
  image_description,
314
  narration,
@@ -316,36 +366,19 @@ with gr.Blocks() as demo:
316
  dialouge,
317
  location,
318
  setting,
319
- chars,
320
  prompt_1,
321
  prompt_2,
322
  prompt_3,
323
  prompt_4,
324
- ],
325
- outputs=[current_scene],
326
- )
327
- regenerate_btn.click(
328
- core.regenerate_data,
329
- inputs=[
330
- comic_id,
331
- current_episode,
332
- current_frame,
333
- episodes_data,
334
  character_data,
335
- visual_style,
336
- height,
337
- width,
338
- details,
339
- chars,
340
- ],
341
- outputs=[images, image_data_b64],
342
  )
343
 
344
- save_btn.click(
345
- core.save_images,
346
- inputs=[image_data_b64, current_episode, current_frame, comic_id, details],
347
- )
348
  demo.launch(
349
- auth=("admin", "Qrt@12*34#immersfy"), share=True, ssr_mode=False, debug=True
 
350
  )
351
  # demo.launch(share=True, ssr_mode=False, debug=True)
 
11
  current_episode = gr.State(-1)
12
  current_scene = gr.State(-1)
13
  current_frame = gr.State(-1)
14
+ episode_data = gr.State({})
15
  character_data = gr.State({})
16
  current_frame_data = gr.State(None)
17
+ frame_hash_map = gr.State({})
18
  image_data_b64 = gr.State([])
19
  choice = gr.State([])
20
 
 
34
  with gr.Column():
35
  load_images = gr.Button("Load Images")
36
  developer = gr.Checkbox(
37
+ value=False, label="Enable Developer Mode"
38
  )
39
 
40
  images = gr.Gallery(
41
+ label="Select an Image", elem_id="image_select", columns=4, height=320, object_fit="contain"
42
  )
43
 
44
  with gr.Row():
 
58
 
59
  with gr.Column(visible=False) as developer_options:
60
  with gr.Column():
61
+ setting = gr.Textbox(label="Frame Setting", interactive=True)
62
  with gr.Row():
63
  with gr.Column():
64
  gr.Markdown("## Composition #1")
65
+ prompt_1 = gr.TextArea(label="Image Prompt", interactive=True)
66
  seed_1 = gr.Textbox(label="Generation Seed")
67
  with gr.Column():
68
  gr.Markdown("## Composition #2")
69
+ prompt_2 = gr.TextArea(label="Image Prompt", interactive=True)
70
  seed_2 = gr.Textbox(label="Generation Seed")
71
  with gr.Row():
72
  with gr.Column():
73
  gr.Markdown("## Composition #3")
74
+ prompt_3 = gr.TextArea(label="Image Prompt", interactive=True)
75
  seed_3 = gr.Textbox(label="Generation Seed")
76
  with gr.Column():
77
  gr.Markdown("## Composition #4")
78
+ prompt_4 = gr.TextArea(label="Image Prompt", interactive=True)
79
  seed_4 = gr.Textbox(label="Generation Seed")
80
 
81
  with gr.Row():
82
  regenerate_comps_btn = gr.Button(value="Regenerate Compositions")
 
83
 
84
  with gr.Column():
85
  negative_prompt = gr.TextArea(
86
  value="",
87
  label="Negative Prompt",
88
+ interactive=True,
89
  )
90
+ related_chars = gr.CheckboxGroup(
 
91
  choices=[], value=[], label="Related Characters", interactive=True
92
  )
93
 
94
  with gr.Row():
95
+ height = gr.Textbox(
96
+ value=parameters.IMG_HEIGHT, label="Image Height", interactive=True
97
+ )
98
+ width = gr.Textbox(
99
+ value=parameters.IMG_WIDTH, label="Image Width", interactive=True
100
+ )
101
  visual_style = gr.Dropdown(
102
  choices=parameters.VISUAL_CHOICES,
103
  label="Current Visual Style",
 
109
  save_btn = gr.Button("Save")
110
 
111
  ############################################ EVENTS ############################################
112
+ developer.change(
113
+ core.toggle_developer_options,
114
+ inputs=[developer],
115
+ outputs=[developer_options],
116
+ ).then(
117
+ lambda is_developer: (
118
+ gr.update(interactive=is_developer),
119
+ gr.update(interactive=is_developer),
120
+ gr.update(interactive=is_developer),
121
+ gr.update(interactive=is_developer),
122
+ gr.update(interactive=is_developer),
123
+ ),
124
+ inputs=[developer],
125
+ outputs=[image_description, narration, character, dialouge, location],
126
+ )
127
+
128
  load_metadata.click(
129
  core.load_metadata_fn,
130
  inputs=[comic_id],
131
+ outputs=[episode_dropdown, current_episode, character_data],
132
+ ).then( # This ensures `load_dropdown_fn` runs after `load_metadata_fn`
133
+ core.episode_dropdown_effect,
134
+ inputs=[comic_id, episode_dropdown],
135
  outputs=[
 
136
  frame_dropdown,
137
  current_episode,
138
  current_frame,
139
+ episode_data,
140
+ frame_hash_map,
 
 
141
  ],
142
  )
143
 
144
  episode_dropdown.input(
145
+ core.episode_dropdown_effect,
146
+ inputs=[comic_id, episode_dropdown],
147
  outputs=[
 
148
  frame_dropdown,
149
  current_episode,
150
  current_frame,
151
+ episode_data,
152
+ frame_hash_map,
153
  ],
154
  )
155
 
156
  frame_dropdown.input(
157
+ lambda frame: frame,
158
  inputs=[frame_dropdown],
159
+ outputs=[current_frame],
 
 
160
  )
161
 
162
  load_images.click(
163
+ core.load_data,
164
  inputs=[
165
+ episode_data,
 
166
  current_frame,
167
+ frame_hash_map,
 
168
  ],
169
  outputs=[
 
 
 
170
  images,
 
 
 
171
  image_description,
172
  narration,
173
  character,
 
182
  seed_3,
183
  prompt_4,
184
  seed_4,
 
185
  ],
186
+ ).then(
187
+ core.update_characters,
188
+ inputs=[character_data, current_frame, frame_hash_map, episode_data],
189
+ outputs=[related_chars],
190
  )
191
 
192
  # When an image is clicked
193
  def get_select_index(evt: gr.SelectData, images):
194
  return images[evt.index]
 
195
  images.select(get_select_index, images, selected_image)
196
 
197
  next_button.click(
198
  core.load_data_next,
199
  inputs=[
200
+ comic_id,
201
  current_episode,
202
  current_frame,
203
+ frame_hash_map,
204
+ episode_data,
205
  ],
206
  outputs=[
207
  episode_dropdown,
208
  frame_dropdown,
 
 
 
209
  current_episode,
210
  current_frame,
211
+ episode_data,
212
+ frame_hash_map,
213
+ ],
214
+ ).then(
215
+ core.load_data,
216
+ inputs=[
217
+ episode_data,
218
+ current_frame,
219
+ frame_hash_map,
220
+ ],
221
+ outputs=[
222
+ images,
223
  image_description,
224
  narration,
225
  character,
 
234
  seed_3,
235
  prompt_4,
236
  seed_4,
 
237
  ],
238
+ ).then(
239
+ core.update_characters,
240
+ inputs=[character_data, current_frame, frame_hash_map, episode_data],
241
+ outputs=[related_chars],
242
+ ).then(
243
+ lambda a, b: (a, b),
244
+ inputs=[current_episode, current_frame],
245
+ outputs=[current_episode, current_frame],
246
  )
247
 
248
  prev_button.click(
249
  core.load_data_prev,
250
  inputs=[
251
+ comic_id,
252
  current_episode,
253
  current_frame,
254
+ frame_hash_map,
255
+ episode_data,
256
  ],
257
  outputs=[
258
  episode_dropdown,
259
  frame_dropdown,
 
 
 
260
  current_episode,
261
  current_frame,
262
+ episode_data,
263
+ frame_hash_map,
264
+ ],
265
+ ).then(
266
+ core.load_data,
267
+ inputs=[
268
+ episode_data,
269
+ current_frame,
270
+ frame_hash_map,
271
+ ],
272
+ outputs=[
273
+ images,
274
  image_description,
275
  narration,
276
  character,
 
285
  seed_3,
286
  prompt_4,
287
  seed_4,
 
288
  ],
289
+ ).then(
290
+ core.update_characters,
291
+ inputs=[character_data, current_frame, frame_hash_map, episode_data],
292
+ outputs=[related_chars],
293
+ ).then(
294
+ lambda a, b: (a, b),
295
+ inputs=[current_episode, current_frame],
296
+ outputs=[current_episode, current_frame],
297
  )
298
 
299
  save_button.click(
 
307
  outputs=[],
308
  )
309
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  regenerate_comps_btn.click(
311
+ core.regenerate_compositions,
312
  inputs=[
313
  image_description,
314
  narration,
 
316
  dialouge,
317
  location,
318
  setting,
319
+ related_chars,
320
  current_episode,
321
  current_frame,
322
+ episode_data,
323
+ frame_hash_map,
324
+ character_data,
325
  ],
326
  outputs=[
327
+ prompt_1,
328
+ prompt_2,
329
+ prompt_3,
330
+ prompt_4,
331
+ ],
332
+ )
333
+
334
+ regenerate_btn.click(
335
+ core.regenerate_images,
336
+ inputs=[
337
+ current_episode,
338
+ current_frame,
339
+ visual_style,
340
+ height,
341
+ width,
342
+ character_data,
343
+ related_chars,
344
  prompt_1,
345
  seed_1,
346
  prompt_2,
 
348
  prompt_3,
349
  seed_3,
350
  prompt_4,
351
+ seed_4
352
  ],
353
+ outputs=[images],
354
  )
355
 
356
+ save_btn.click(
357
+ core.save_comic_data,
358
  inputs=[
359
  current_episode,
360
  current_frame,
361
+ episode_data,
362
  comic_id,
363
  image_description,
364
  narration,
 
366
  dialouge,
367
  location,
368
  setting,
 
369
  prompt_1,
370
  prompt_2,
371
  prompt_3,
372
  prompt_4,
373
+ frame_hash_map,
374
+ related_chars,
 
 
 
 
 
 
 
 
375
  character_data,
376
+ images,
377
+ ]
 
 
 
 
 
378
  )
379
 
 
 
 
 
380
  demo.launch(
381
+ # auth=("admin", "Qrt@12*34#immersfy"), share=True, ssr_mode=False, debug=True
382
+ share=False,
383
  )
384
  # demo.launch(share=True, ssr_mode=False, debug=True)
aws_utils.py CHANGED
@@ -84,7 +84,7 @@ def fetch_from_s3(source: Union[str, dict], region_name: str = "ap-south-1") ->
84
  Returns:
85
  bytes: The content of the file fetched from S3.
86
  """
87
- print(f"Fetching file from S3. Source: {source}")
88
  s3_client = boto3.client("s3", region_name=region_name)
89
 
90
  # Parse the source depending on its type
@@ -104,7 +104,7 @@ def fetch_from_s3(source: Union[str, dict], region_name: str = "ap-south-1") ->
104
  print("Source must be a string URL or a dictionary.")
105
  raise ValueError("Source must be a string URL or a dictionary.")
106
 
107
- print(f"Attempting to download from bucket: {bucket_name}, path: {file_path}")
108
  try:
109
  response = s3_client.get_object(Bucket=bucket_name, Key=file_path)
110
  file_content = response["Body"].read()
 
84
  Returns:
85
  bytes: The content of the file fetched from S3.
86
  """
87
+ # print(f"Fetching file from S3. Source: {source}")
88
  s3_client = boto3.client("s3", region_name=region_name)
89
 
90
  # Parse the source depending on its type
 
104
  print("Source must be a string URL or a dictionary.")
105
  raise ValueError("Source must be a string URL or a dictionary.")
106
 
107
+ # print(f"Attempting to download from bucket: {bucket_name}, path: {file_path}")
108
  try:
109
  response = s3_client.get_object(Bucket=bucket_name, Key=file_path)
110
  file_content = response["Body"].read()
core.py CHANGED
@@ -1,7 +1,7 @@
1
  from typing import List
2
  from PIL import Image
 
3
  import gradio as gr
4
- import dataclasses
5
  import io
6
  import jinja2
7
  import base64
@@ -11,38 +11,20 @@ import script_gen
11
  import inout as iowrapper
12
  import openai_wrapper
13
  import json
14
- from dataclasses import asdict
15
  import base64
16
- import io
17
 
18
  AWS_BUCKET = parameters.AWS_BUCKET
19
  llm = openai_wrapper.GPT_4O_MINI
20
 
21
 
22
- @dataclasses.dataclass
23
- class Composition:
24
- prompt: str
25
- shot_type: str
26
- seed: int
27
- image: str
28
-
29
-
30
- @dataclasses.dataclass
31
- class ComicFrame:
32
- description: str
33
- narration: str
34
- character_dilouge: str
35
- character: str
36
- location: str
37
- setting: str
38
- all_characters: list
39
- compositions: List[Composition] = dataclasses.field(default_factory=list)
40
-
41
-
42
- def update_characters(character_data, curr_frame):
43
- return gr.CheckboxGroup(
44
- choices=list(character_data.keys()), value=curr_frame.all_characters
45
- )
46
 
47
 
48
  def list_current_dir(bucket_name: str, folder_path: str = "") -> list:
@@ -59,227 +41,183 @@ def list_current_dir(bucket_name: str, folder_path: str = "") -> list:
59
  return []
60
 
61
 
62
- def load_data_inner(
63
- episodes_data: list,
64
- current_episode: int,
65
- current_frame: int,
66
- is_developer: bool,
67
- character_data,
68
- ):
69
  try:
70
- images = []
71
- curr_frame = episodes_data[current_episode][current_frame]
72
- for comp in curr_frame.compositions:
73
- data = aws_utils.fetch_from_s3(comp.image)
74
- images.append(Image.open(io.BytesIO(data)))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  return (
76
- images,
77
- episodes_data,
78
- current_episode,
79
- current_frame,
80
- gr.Textbox(value=curr_frame.description, interactive=is_developer),
81
- gr.Textbox(value=curr_frame.narration, interactive=is_developer),
82
- gr.Textbox(value=curr_frame.character, interactive=is_developer),
83
- gr.Textbox(value=curr_frame.character_dilouge, interactive=is_developer),
84
- gr.Textbox(value=curr_frame.location, interactive=is_developer),
85
- curr_frame.setting,
86
- curr_frame.compositions[0].prompt,
87
- curr_frame.compositions[0].seed,
88
- curr_frame.compositions[1].prompt,
89
- curr_frame.compositions[1].seed,
90
- curr_frame.compositions[2].prompt,
91
- curr_frame.compositions[2].seed,
92
- curr_frame.compositions[3].prompt,
93
- curr_frame.compositions[3].seed,
94
- update_characters(character_data, curr_frame),
95
  )
96
  except Exception as e:
97
- return (
98
- [],
99
- episodes_data,
100
- current_episode,
101
- current_frame,
102
- gr.Textbox(),
103
- gr.Textbox(),
104
- gr.Textbox(),
105
- gr.Textbox(),
106
- gr.Textbox(),
107
- "",
108
- "",
109
- "",
110
- "",
111
- "",
112
- "",
113
- "",
114
- "",
115
- "",
116
- [],
 
 
 
 
117
  )
 
 
 
118
 
119
 
120
- def load_metadata_fn(comic_id: str):
 
 
 
 
 
 
 
 
 
 
 
 
121
  try:
122
- episodes_data = {}
123
- episode_idx = []
124
- character_data = {}
125
- details = {}
126
- character_path = f"s3://blix-demo-v0/{comic_id}/characters/characters.json"
127
- char_data = eval(aws_utils.fetch_from_s3(source=character_path).decode("utf-8"))
128
 
129
- for name, char in char_data.items():
130
- character_data[name] = char["profile_image"]
 
 
 
 
 
 
131
 
132
- for folder in list_current_dir(AWS_BUCKET, f"{comic_id}/episodes/"):
133
- if "episode" in folder:
134
- json_path = f"s3://{AWS_BUCKET}/{folder}episode.json"
135
- idx = int(folder.split("/")[2].split("-")[-1])
136
- episode_idx.append(idx)
137
- data = eval(aws_utils.fetch_from_s3(source=json_path).decode("utf-8"))
138
- comic_frames = []
139
- details[idx] = {}
140
- cumulative_frame_count = 0
141
-
142
- for scene_num, scene in enumerate(data["scenes"]):
143
- scene_frame_count = len(scene["frames"])
144
- cumulative_frame_count += scene_frame_count
145
- details[idx][scene_num] = cumulative_frame_count
146
-
147
- for frame in scene["frames"]:
148
- comic_frames.append(
149
- ComicFrame(
150
- description=frame["description"],
151
- narration=frame["narration"],
152
- character=frame["audio_cue_character"],
153
- character_dilouge=frame["audio_cue_text"],
154
- compositions=[
155
- Composition(**comp)
156
- for comp in frame["compositions"]
157
- ],
158
- location=frame["location"],
159
- setting=frame["frame_setting"],
160
- all_characters=[
161
- char["name"] for char in frame["characters"]
162
- ],
163
- )
164
- )
165
- episodes_data[idx] = comic_frames
166
-
167
- current_episode, current_frame = min(episode_idx), 0
168
  return (
169
- gr.update(choices=episode_idx, value=episode_idx[0]),
170
- gr.update(
171
- choices=range(len(episodes_data[current_episode])), value=current_frame
172
- ),
173
- current_episode,
174
- current_frame,
175
- episodes_data,
176
- character_data,
177
- details,
178
- gr.Checkbox(visible=True),
 
 
 
 
 
179
  )
180
  except Exception as e:
181
- return (
182
- gr.update(choices=[]),
183
- gr.update(choices=[]),
184
- {},
185
- {},
186
- {},
187
- gr.Checkbox(visible=False),
188
- )
 
 
 
 
 
 
189
 
190
 
191
  def load_data_next(
192
- episodes_data: list,
193
  current_episode: int,
194
  current_frame: int,
195
- is_developer: bool,
196
- character_data,
197
  ):
198
- if current_frame + 1 < len(episodes_data[current_episode]):
199
  current_frame += 1
200
- elif current_episode + 1 < len(episodes_data):
201
- current_episode += 1
202
- current_frame = 0
203
  else:
204
- return [], current_episode, current_frame
 
 
 
 
 
205
  return (
206
  gr.update(value=current_episode),
207
- gr.update(value=current_frame),
208
- *load_data_inner(
209
- episodes_data, current_episode, current_frame, is_developer, character_data
210
- ),
 
211
  )
212
 
213
 
214
  def load_data_prev(
215
- episodes_data: list,
216
  current_episode: int,
217
  current_frame: int,
218
- is_developer: bool,
219
- character_data,
220
  ):
221
- if current_frame - 1 >= 0:
222
  current_frame -= 1
223
- elif current_episode - 1 > min(list(episodes_data.keys())):
224
- current_episode -= 1
225
- current_frame = 0
226
  else:
227
- return [], current_episode, current_frame
 
 
 
 
 
228
  return (
229
  gr.update(value=current_episode),
230
- gr.update(value=current_frame),
231
- *load_data_inner(
232
- episodes_data, current_episode, current_frame, is_developer, character_data
233
- ),
 
234
  )
235
 
236
 
237
- def load_from_dropdown(
238
- episodes_data: dict,
239
- selected_episode: int,
240
- selected_frame: int,
241
- is_developer: bool,
242
- character_data,
243
  ):
244
- return (
245
- gr.update(value=selected_episode),
246
- gr.update(value=selected_frame),
247
- *load_data_inner(
248
- episodes_data,
249
- selected_episode,
250
- selected_frame,
251
- is_developer,
252
- character_data,
253
- ),
254
- # update_characters(character_data)
255
- )
256
-
257
-
258
- def get_scene_number(details, current_episode, current_frame):
259
- episode_details = details.get(current_episode)
260
- if not episode_details:
261
- print(f"Episode {current_episode} not found!")
262
- return None
263
-
264
- # Determine scene number and frame number within the scene
265
- scene_num, frame_num_in_scene = None, 0
266
- for scene_idx, cumulative_frame_count in enumerate(episode_details.items()):
267
- if current_frame < cumulative_frame_count[1]:
268
- scene_num = cumulative_frame_count[0]
269
- frame_num_in_scene = current_frame - (episode_details.get(scene_num - 1, 0))
270
- break
271
- return scene_num, frame_num_in_scene
272
-
273
-
274
- def load_dropdown_fn(selected_episode):
275
- return (gr.update(value=selected_episode), gr.update(value=0), selected_episode, 0)
276
-
277
-
278
- def load_dropdown_fn_v2(selected_frame):
279
- return selected_frame
280
-
281
-
282
- def save_image(selected_image, comic_id: str, current_episode: int, current_frame: int):
283
  with Image.open(selected_image[0]) as img:
284
  img_bytes = io.BytesIO()
285
  img.convert("RGB").save(img_bytes, "JPEG")
@@ -293,224 +231,201 @@ def save_image(selected_image, comic_id: str, current_episode: int, current_fram
293
  gr.Info("Saved Image successfully!")
294
 
295
 
296
- def toggle_developer_options(
297
- is_developer: bool, prompt_1, prompt_2, prompt_3, prompt_4, setting
298
- ):
299
- if is_developer:
300
- # Return visibility updates for the developer options along with the values
301
- return gr.update(visible=True), prompt_1, prompt_2, prompt_3, prompt_4, setting
302
- else:
303
- # Hide the developer options and return only the updated visibility
304
- return gr.update(visible=False), prompt_1, prompt_2, prompt_3, prompt_4, setting
305
-
306
-
307
- def regenerate_composition_data(
308
- image_description,
309
- narration,
310
- character,
311
- dialouge,
312
- location,
313
- setting,
314
- chars,
315
  current_episode: int,
316
  current_frame: int,
317
  episodes_data: dict,
 
 
318
  ):
319
  try:
320
  print(
321
  f"Regenerating composition data for episode {current_episode}, frame {current_frame}"
322
  )
323
- frame = episodes_data[current_episode][current_frame]
324
-
325
- try:
326
- print("Creating prompt template for composition generation")
327
- prompt_template = jinja2.Template(
328
- script_gen.generate_image_compositions_user_prompt
329
- )
330
- except Exception as e:
331
- print(f"Error creating prompt template: {e}")
332
- raise
333
 
334
  try:
335
- print("Rendering prompt with frame details")
336
  prompt_dict = {
337
  "system": script_gen.generate_image_compositions_instruction,
338
- "user": prompt_template.render(
 
 
339
  {
340
  "FRAME": {
341
  "description": image_description,
342
  "narration": narration,
343
- "character_dilouge": dialouge,
344
- "character": character,
345
  "location": location,
346
- "setting": setting,
347
- "all_characters": chars,
348
- }
 
 
349
  }
350
  ),
351
  }
352
- except Exception as e:
353
- print(f"Error rendering prompt: {e}")
354
- raise
355
 
356
- try:
357
  print("Generating compositions using LLM")
358
  compositions = llm.generate_valid_json_response(prompt_dict)
359
- except Exception as e:
360
- print(f"Error generating compositions: {e}")
361
- raise
362
-
363
- try:
364
- print("Updating frame compositions")
365
- frame.compositions = [
366
- Composition(
367
- **comp,
368
- seed=(
369
- frame.compositions[idx].seed
370
- if idx < len(frame.compositions)
371
- else ""
372
- ),
373
- image=(
374
- frame.compositions[idx].image
375
- if idx < len(frame.compositions)
376
- else ""
377
- ),
378
- )
379
- for idx, comp in enumerate(compositions["compositions"])
380
- ]
381
  except Exception as e:
382
  print(f"Error updating frame compositions: {e}")
383
  raise
384
 
385
  print("Composition data regenerated successfully.")
386
  return [
387
- frame.compositions[0].prompt,
388
- frame.compositions[0].seed,
389
- frame.compositions[1].prompt,
390
- frame.compositions[1].seed,
391
- frame.compositions[2].prompt,
392
- frame.compositions[2].seed,
393
- frame.compositions[3].prompt,
394
- frame.compositions[3].seed,
395
  ]
396
  except Exception as e:
397
  print(f"Error in regenerate_composition_data: {e}")
398
  return [""] * 8
399
 
400
 
401
- def regenerate_data(
402
- comic_id,
403
- current_episode,
404
- current_frame,
405
- episodes_data,
406
- character_data,
407
- visual_style,
408
- height,
409
- width,
410
- details,
411
- chars,
 
 
 
 
 
412
  ):
413
- images = []
414
- image_data_b64 = []
415
- print(chars)
416
- try:
417
- current_scene, frame_num_in_scene = get_scene_number(
418
- details, current_episode, current_frame
419
- )
420
  print(
421
- f"Regenerating data for episode {current_episode}, Scene: {current_scene}, frame {frame_num_in_scene}"
422
  )
423
- frame = episodes_data[current_episode][current_frame]
424
- related_chars = [character_data[ch] for ch in chars]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
 
426
- for i, composition in enumerate(frame.compositions):
427
  try:
428
  print(f"Generating image for composition {i}")
 
 
 
 
 
429
  payload = {
430
- "prompt": composition.prompt,
431
  "characters": related_chars,
432
  "parameters": {
433
  "height": height,
434
  "width": width,
435
  "visual_style": visual_style,
436
- "seed": composition.seed,
437
  },
438
  }
439
 
440
- try:
441
- print(f"Sending request to generate image for composition {i}")
442
- data = iowrapper.get_valid_post_response(
443
- url=f"{parameters.MODEL_SERVER_URL}generate_image",
444
- payload=payload,
445
- )
446
- print(f"Image generated for composition {i}. Decoding image data.")
447
- image_data = io.BytesIO(base64.b64decode(data["image"]))
448
- image_data_b64.append(data["image"])
449
- images.append(Image.open(image_data))
450
- except Exception as e:
451
- print(f"Error generating image for composition {i}: {e}")
452
- continue
453
  except Exception as e:
454
  print(f"Error processing composition {i}: {e}")
455
  continue
456
 
457
- print("Data regeneration completed.")
458
- return images, image_data_b64
 
459
  except Exception as e:
460
  print(f"Error in regenerate_data: {e}")
461
- return [], []
 
462
 
463
 
464
- def save_image_compositions(
465
  current_episode: int,
466
  current_frame: int,
467
- details: dict,
468
  comic_id: str,
469
- image_description,
470
- narration,
471
- character,
472
- dialogue, # Fixed typo from 'dialouge' to 'dialogue'
473
- location,
474
- setting,
475
- chars,
476
- prompt_1,
477
- prompt_2,
478
- prompt_3,
479
- prompt_4,
 
 
 
480
  ):
481
  try:
482
- current_scene, frame_num_in_scene = get_scene_number(
483
- details, current_episode, current_frame
 
484
  )
 
485
  print(
486
- f"Saving image components for episode {current_episode}, frame {frame_num_in_scene}"
487
- )
488
-
489
- # Fetch episode data from S3
490
- episode_path = f"s3://blix-demo-v0/{comic_id}/episodes/episode-{current_episode}/episode.json"
491
- print(f"Fetching episode from S3: {episode_path}")
492
- episode_json = aws_utils.fetch_from_s3(episode_path).decode("utf-8")
493
- episode = json.loads(episode_json)
494
-
495
- frame_data = episode["scenes"][current_scene]["frames"][frame_num_in_scene]
496
- print(
497
- f"Updating compositions for scene {current_scene}, frame {frame_num_in_scene}"
498
  )
499
 
500
  # Update compositions with prompts
501
  prompts_list = [prompt_1, prompt_2, prompt_3, prompt_4]
502
- frame_data["compositions"] = [
503
- {
504
- "prompt": prompts_list[i],
505
- "shot_type": comp["shot_type"],
506
- "seed": comp["seed"],
507
- "image": comp["image"],
508
- }
509
- for i, comp in enumerate(frame_data["compositions"])
510
- ]
 
 
 
 
511
 
512
- # Batch update frame data
513
- frame_data.update(
514
  {
515
  "description": image_description,
516
  "narration": narration,
@@ -518,48 +433,19 @@ def save_image_compositions(
518
  "location": location,
519
  "setting": setting,
520
  "audio_cue_character": character,
 
521
  }
522
  )
523
 
524
  # Save the updated episode back to S3
525
- print(f"Saving updated episode to S3 at {episode_path}")
526
  aws_utils.save_to_s3(
527
  bucket_name=parameters.AWS_BUCKET,
528
  folder_name=f"{comic_id}/episodes/episode-{current_episode}",
529
- content=json.dumps(episode),
530
  file_name="episode.json",
531
  )
532
-
533
- gr.Info("Compositions saved successfully!")
534
- print(current_scene)
535
- return current_scene
536
-
537
- except Exception as e:
538
- print(f"Error in save_image_compositions: {e}")
539
- return None
540
-
541
-
542
- def save_images(image_data_b64, current_episode, current_frame, comic_id, details):
543
- try:
544
- current_scene, frame_num_in_scene = get_scene_number(
545
- details, current_episode, current_frame
546
- )
547
- print(
548
- f"Saving images for episode {current_episode}, Scene {current_scene} ,frame {frame_num_in_scene}."
549
- )
550
- for i, image_data in enumerate(image_data_b64):
551
- try:
552
- print(f"Saving image {i} to S3")
553
- aws_utils.save_to_s3(
554
- parameters.AWS_BUCKET,
555
- f"{comic_id}/episodes/episode-{current_episode}/compositions/scene-{current_scene}/frame-{frame_num_in_scene}",
556
- io.BytesIO(base64.b64decode(image_data)),
557
- f"{i}.jpg",
558
- )
559
- except Exception as e:
560
- print(f"Error saving image {i} to S3: {e}")
561
- continue
562
- print("All images saved successfully!")
563
- gr.Info("All images saved successfully!")
564
  except Exception as e:
565
- print(f"Error in save_images: {e}")
 
 
1
  from typing import List
2
  from PIL import Image
3
+ import json
4
  import gradio as gr
 
5
  import io
6
  import jinja2
7
  import base64
 
11
  import inout as iowrapper
12
  import openai_wrapper
13
  import json
 
14
  import base64
 
15
 
16
  AWS_BUCKET = parameters.AWS_BUCKET
17
  llm = openai_wrapper.GPT_4O_MINI
18
 
19
 
20
+ #### Functions ordered by their order of developement.
21
+ def toggle_developer_options(is_developer: bool):
22
+ if is_developer:
23
+ # Return visibility updates for the developer options along with the values
24
+ return gr.update(visible=True)
25
+ else:
26
+ # Hide the developer options and return only the updated visibility
27
+ return gr.update(visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
 
30
  def list_current_dir(bucket_name: str, folder_path: str = "") -> list:
 
41
  return []
42
 
43
 
44
+ def load_metadata_fn(comic_id: str):
 
 
 
 
 
 
45
  try:
46
+ # Load character data
47
+ character_data = {}
48
+ character_path = f"s3://blix-demo-v0/{comic_id}/characters/characters.json"
49
+ char_data = json.loads(aws_utils.fetch_from_s3(character_path).decode("utf-8"))
50
+ character_data = {
51
+ name: char for name, char in char_data.items()
52
+ }
53
+
54
+ # Load episode data
55
+ episode_folders = list_current_dir(AWS_BUCKET, f"{comic_id}/episodes/")
56
+ episode_indices = []
57
+ for folder in episode_folders:
58
+ if "episode" in folder:
59
+ idx = int(folder.split("/")[2].split("-")[-1])
60
+ episode_indices.append(idx)
61
+ if not episode_indices:
62
+ return (gr.update(choices=[]), None, {})
63
+
64
+ # Return the values
65
+ min_episode = min(episode_indices)
66
  return (
67
+ gr.update(choices=episode_indices, value=min_episode),
68
+ min_episode,
69
+ character_data,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  )
71
  except Exception as e:
72
+ gr.Warning(f"Error loading metadata: {e}")
73
+ return (gr.update(choices=[]), None, {})
74
+
75
+
76
+ def load_episode_data(comic_id: str, episode_num: int):
77
+ try:
78
+ print(f"For episode: {episode_num}")
79
+ json_path = (
80
+ f"s3://{AWS_BUCKET}/{comic_id}/episodes/episode-{episode_num}/episode.json"
81
+ )
82
+ episode_data = json.loads(aws_utils.fetch_from_s3(json_path).decode("utf-8"))
83
+ frame_hash_map = {}
84
+ count = 1
85
+ for scene_idx, scene in enumerate(episode_data["scenes"]):
86
+ for frame_idx, _ in enumerate(scene["frames"]):
87
+ frame_hash_map[count] = {
88
+ "scene": scene_idx,
89
+ "frame": frame_idx,
90
+ }
91
+ count += 1
92
+ return (episode_data, frame_hash_map)
93
+ except Exception as e:
94
+ print(
95
+ f"Failed to load json dictionary for episode: {episode_num} at path: {json_path}"
96
  )
97
+ import traceback as tc
98
+ print(tc.format_exc())
99
+ return {}, {}
100
 
101
 
102
+ def episode_dropdown_effect(comic_id, selected_episode):
103
+ episode_data, frame_hash_map = load_episode_data(comic_id, selected_episode)
104
+ current_frame = min(list(frame_hash_map.keys()))
105
+ return (
106
+ gr.update(choices=list(frame_hash_map.keys()), value=current_frame),
107
+ selected_episode,
108
+ current_frame,
109
+ episode_data,
110
+ frame_hash_map,
111
+ )
112
+
113
+
114
+ def load_data(episodes_data: dict, current_frame: int, frame_hash_map: dict):
115
  try:
116
+ image_list = []
117
+ scene_num, frame_num = (
118
+ frame_hash_map[current_frame]["scene"],
119
+ frame_hash_map[current_frame]["frame"],
120
+ )
121
+ curr_frame = episodes_data["scenes"][scene_num]["frames"][frame_num]
122
 
123
+ for comp in curr_frame["compositions"]:
124
+ # Fetch image from S3
125
+ data = aws_utils.fetch_from_s3(comp["image"])
126
+ if data:
127
+ image = Image.open(io.BytesIO(data))
128
+ image_list.append(image)
129
+ else:
130
+ print(f"Failed to load image from: {comp['image']}")
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  return (
133
+ image_list, # Return the image list to be displayed in the gallery
134
+ curr_frame["description"],
135
+ curr_frame["narration"],
136
+ curr_frame["audio_cue_character"],
137
+ curr_frame["audio_cue_text"],
138
+ curr_frame["location"],
139
+ curr_frame["frame_setting"],
140
+ curr_frame["compositions"][0]["prompt"],
141
+ curr_frame["compositions"][0]["seed"],
142
+ curr_frame["compositions"][1]["prompt"],
143
+ curr_frame["compositions"][1]["seed"],
144
+ curr_frame["compositions"][2]["prompt"],
145
+ curr_frame["compositions"][2]["seed"],
146
+ curr_frame["compositions"][3]["prompt"],
147
+ curr_frame["compositions"][3]["seed"],
148
  )
149
  except Exception as e:
150
+ print("Error in load_data:", str(e)) # Debugging the error
151
+ gr.Warning("Failed to load data. Check logs!")
152
+
153
+
154
+ def update_characters(character_data: dict, current_frame: int, frame_hash_map: dict, episode_data: dict):
155
+ scene_num, frame_num = (
156
+ frame_hash_map[current_frame]["scene"],
157
+ frame_hash_map[current_frame]["frame"],
158
+ )
159
+ curr_frame = episode_data["scenes"][scene_num]["frames"][frame_num]
160
+ return gr.CheckboxGroup(
161
+ choices=list(character_data.keys()),
162
+ value=[char["name"] for char in curr_frame["characters"]],
163
+ )
164
 
165
 
166
  def load_data_next(
167
+ comic_id: str,
168
  current_episode: int,
169
  current_frame: int,
170
+ frame_hash_map: dict,
171
+ episode_data: dict,
172
  ):
173
+ if current_frame + 1 < list(frame_hash_map.keys())[-1]:
174
  current_frame += 1
 
 
 
175
  else:
176
+ current_episode += 1
177
+ episode_data, frame_hash_map = load_episode_data(comic_id, current_episode)
178
+ if len(episode_data) < 1:
179
+ gr.Warning("All episodes finished.")
180
+ return
181
+ current_frame = min(list(frame_hash_map.keys()))
182
  return (
183
  gr.update(value=current_episode),
184
+ gr.update(choices=list(frame_hash_map.keys()), value=current_frame),
185
+ current_episode,
186
+ current_frame,
187
+ episode_data,
188
+ frame_hash_map,
189
  )
190
 
191
 
192
  def load_data_prev(
193
+ comic_id: str,
194
  current_episode: int,
195
  current_frame: int,
196
+ frame_hash_map: dict,
197
+ episode_data: dict,
198
  ):
199
+ if current_frame - 1 >= list(frame_hash_map.keys())[0]:
200
  current_frame -= 1
 
 
 
201
  else:
202
+ current_episode -= 1
203
+ episode_data, frame_hash_map = load_episode_data(comic_id, current_episode)
204
+ if len(episode_data) < 1:
205
+ gr.Warning("No previous episode found.")
206
+ return
207
+ current_frame = min(list(frame_hash_map.keys()))
208
  return (
209
  gr.update(value=current_episode),
210
+ gr.update(choices=list(frame_hash_map.keys()), value=current_frame),
211
+ current_episode,
212
+ current_frame,
213
+ episode_data,
214
+ frame_hash_map,
215
  )
216
 
217
 
218
+ def save_image(
219
+ selected_image: ..., comic_id: str, current_episode: int, current_frame: int
 
 
 
 
220
  ):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  with Image.open(selected_image[0]) as img:
222
  img_bytes = io.BytesIO()
223
  img.convert("RGB").save(img_bytes, "JPEG")
 
231
  gr.Info("Saved Image successfully!")
232
 
233
 
234
+ def regenerate_compositions(
235
+ image_description: str,
236
+ narration: str,
237
+ character: str,
238
+ dialouge: str,
239
+ location: str,
240
+ setting: str,
241
+ rel_chars: list,
 
 
 
 
 
 
 
 
 
 
 
242
  current_episode: int,
243
  current_frame: int,
244
  episodes_data: dict,
245
+ frame_hash_map: dict,
246
+ character_data: dict,
247
  ):
248
  try:
249
  print(
250
  f"Regenerating composition data for episode {current_episode}, frame {current_frame}"
251
  )
252
+ scene_num, frame_num = (
253
+ frame_hash_map[current_frame]["scene"],
254
+ frame_hash_map[current_frame]["frame"],
255
+ )
256
+ prev_frame = {}
257
+ if frame_num-1 > 0:
258
+ prev_frame = episodes_data["scenes"][scene_num]["frames"][frame_num-1]
 
 
 
259
 
260
  try:
261
+ related_chars = [character_data[char] for char in rel_chars]
262
  prompt_dict = {
263
  "system": script_gen.generate_image_compositions_instruction,
264
+ "user": jinja2.Template(
265
+ script_gen.generate_image_compositions_user_prompt
266
+ ).render(
267
  {
268
  "FRAME": {
269
  "description": image_description,
270
  "narration": narration,
271
+ "audio_cue_text": dialouge,
272
+ "audio_cue_character": character,
273
  "location": location,
274
+ "frame_setting": setting,
275
+ "characters": json.dumps(related_chars),
276
+ },
277
+ "LOCATION_DESCRIPTION": prev_frame.get("location", ""),
278
+ "frame_settings": prev_frame.get("frame_setting", ""),
279
  }
280
  ),
281
  }
 
 
 
282
 
 
283
  print("Generating compositions using LLM")
284
  compositions = llm.generate_valid_json_response(prompt_dict)
285
+ comps = compositions["compositions"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  except Exception as e:
287
  print(f"Error updating frame compositions: {e}")
288
  raise
289
 
290
  print("Composition data regenerated successfully.")
291
  return [
292
+ comps[0]["prompt"],
293
+ comps[1]["prompt"],
294
+ comps[2]["prompt"],
295
+ comps[3]["prompt"],
 
 
 
 
296
  ]
297
  except Exception as e:
298
  print(f"Error in regenerate_composition_data: {e}")
299
  return [""] * 8
300
 
301
 
302
+ def regenerate_images(
303
+ current_episode: int,
304
+ current_frame: int,
305
+ visual_style: str,
306
+ height: int,
307
+ width: int,
308
+ character_data: dict,
309
+ rel_chars: dict,
310
+ prompt_1: str,
311
+ seed_1: str,
312
+ prompt_2: str,
313
+ seed_2: str,
314
+ prompt_3: str,
315
+ seed_3: str,
316
+ prompt_4: str,
317
+ seed_4: str,
318
  ):
319
+ image_list = []
320
+ try:
 
 
 
 
 
321
  print(
322
+ f"Regenerating data for episode {current_episode}, and frame {current_frame}"
323
  )
324
+ related_chars = [character_data[ch]["profile_image"] for ch in rel_chars]
325
+ new_compositions = [
326
+ {
327
+ "prompt": prompt_1,
328
+ "seed": seed_1,
329
+ },
330
+ {
331
+ "prompt": prompt_2,
332
+ "seed": seed_2,
333
+ },
334
+ {
335
+ "prompt": prompt_3,
336
+ "seed": seed_3,
337
+ },
338
+ {
339
+ "prompt": prompt_4,
340
+ "seed": seed_4,
341
+ },
342
+ ]
343
 
344
+ for i, composition in enumerate(new_compositions):
345
  try:
346
  print(f"Generating image for composition {i}")
347
+ prompt = composition["prompt"]
348
+ if "NOCHAR" in prompt:
349
+ prompt = prompt.replace(
350
+ "NOCHAR", ""
351
+ )
352
  payload = {
353
+ "prompt": prompt,
354
  "characters": related_chars,
355
  "parameters": {
356
  "height": height,
357
  "width": width,
358
  "visual_style": visual_style,
359
+ "seed": composition["seed"],
360
  },
361
  }
362
 
363
+ data = iowrapper.get_valid_post_response(
364
+ url=f"{parameters.MODEL_SERVER_URL}generate_image",
365
+ payload=payload,
366
+ )
367
+ image_list.append(Image.open(io.BytesIO(base64.b64decode(data["image"]))))
 
 
 
 
 
 
 
 
368
  except Exception as e:
369
  print(f"Error processing composition {i}: {e}")
370
  continue
371
 
372
+ print(f"Generated new images for episode: {current_episode} and frame: {current_frame}")
373
+ print(f"Length of image list: {len(image_list)}")
374
+ return image_list
375
  except Exception as e:
376
  print(f"Error in regenerate_data: {e}")
377
+ gr.Warning("Failed to generate new images!")
378
+ return []
379
 
380
 
381
+ def save_comic_data(
382
  current_episode: int,
383
  current_frame: int,
384
+ episode_data: dict,
385
  comic_id: str,
386
+ image_description: str,
387
+ narration: str,
388
+ character: str,
389
+ dialogue: str,
390
+ location: str,
391
+ setting: str,
392
+ prompt_1: str,
393
+ prompt_2: str,
394
+ prompt_3: str,
395
+ prompt_4: str,
396
+ frame_hash_map: dict,
397
+ rel_chars: list,
398
+ character_data: dict,
399
+ images: list
400
  ):
401
  try:
402
+ scene_num, frame_num = (
403
+ frame_hash_map[current_frame]["scene"],
404
+ frame_hash_map[current_frame]["frame"],
405
  )
406
+ curr_frame = episode_data["scenes"][scene_num]["frames"][frame_num]
407
  print(
408
+ f"Saving comic data for episode {current_episode}, frame {frame_num}"
 
 
 
 
 
 
 
 
 
 
 
409
  )
410
 
411
  # Update compositions with prompts
412
  prompts_list = [prompt_1, prompt_2, prompt_3, prompt_4]
413
+ for i, comp in enumerate(curr_frame["compositions"]):
414
+ comp["prompt"] = prompts_list[i]
415
+ # Save new images to S3
416
+ with Image.open(images[i][0]) as img:
417
+ img_bytes = io.BytesIO()
418
+ img.convert("RGB").save(img_bytes, "JPEG")
419
+ img_bytes.seek(0)
420
+ aws_utils.save_to_s3(
421
+ parameters.AWS_BUCKET,
422
+ f"{comic_id}/episodes/episode-{current_episode}/compositions/scene-{scene_num}/frame-{frame_num}",
423
+ img_bytes,
424
+ f"{i}.jpg",
425
+ )
426
 
427
+ # Update frame data
428
+ curr_frame.update(
429
  {
430
  "description": image_description,
431
  "narration": narration,
 
433
  "location": location,
434
  "setting": setting,
435
  "audio_cue_character": character,
436
+ "characters": [character_data[char] for char in rel_chars],
437
  }
438
  )
439
 
440
  # Save the updated episode back to S3
441
+ print(f"Saving updated episode {current_episode} to S3")
442
  aws_utils.save_to_s3(
443
  bucket_name=parameters.AWS_BUCKET,
444
  folder_name=f"{comic_id}/episodes/episode-{current_episode}",
445
+ content=episode_data,
446
  file_name="episode.json",
447
  )
448
+ gr.Info("Comic data saved successfully!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449
  except Exception as e:
450
+ print(f"Error in saving comic data: {e}")
451
+ gr.Warning("Failed to save data for the comic!")
parameters.py CHANGED
@@ -1,7 +1,7 @@
1
  import os
2
- # from dotenv import load_dotenv
3
 
4
- # load_dotenv()
5
 
6
 
7
  AWS_BUCKET = os.getenv("AWS_BUCKET")
@@ -11,4 +11,6 @@ os.environ["S3_BUCKET_NAME"] = os.getenv("AWS_BUCKET")
11
  VISUAL_CHOICES = ["DARK", "FLUX_COMIC", "GHIBLI_COMIC"]
12
  MAX_TRIES = os.getenv("MAX_TRIES")
13
  OPEN_AI_API_KEY = os.getenv("OPEN_AI_KEY")
14
- MODEL_SERVER_URL = os.getenv("MODEL_SERVER_URL")
 
 
 
1
  import os
2
+ from dotenv import load_dotenv
3
 
4
+ load_dotenv()
5
 
6
 
7
  AWS_BUCKET = os.getenv("AWS_BUCKET")
 
11
  VISUAL_CHOICES = ["DARK", "FLUX_COMIC", "GHIBLI_COMIC"]
12
  MAX_TRIES = os.getenv("MAX_TRIES")
13
  OPEN_AI_API_KEY = os.getenv("OPEN_AI_KEY")
14
+ MODEL_SERVER_URL = os.getenv("MODEL_SERVER_URL")
15
+ IMG_HEIGHT=os.getenv("IMG_HEIGHT")
16
+ IMG_WIDTH=os.getenv("IMG_WIDTH")
script_gen.py CHANGED
@@ -1,53 +1,60 @@
1
  generate_image_compositions_instruction = """\
2
- As a visual artist and cinematographer with extensive knowledge of different camera angles, visual styles, and aesthetics, your task is to analyze the provided frame details and create four distinct compositions. Each composition should follow a specific narrative structure in its prompt construction:
3
 
4
  1) Output Requirements in json:
5
  {
6
  "compositions": [
7
  {
8
  "prompt": "Detailed visual description following the structure below (max 77 tokens)",
9
- "shot_type": "Optimal cinematographic shot"
10
  }
11
  ]
12
  }
13
 
14
  2) Prompt Structure (in this specific order):
15
  a) Begin with the environment and setting:
16
- - Establish the broader landscape/location first
17
- - Describe key environmental elements
18
- - Set the atmospheric conditions
19
- - Define the lighting and mood
20
-
21
  b) Then layer in the scene elements:
22
  - How different parts of the environment interact
23
  - Spatial relationships and depth
24
- - Textures and materials
25
  - Any dynamic elements (movement, weather effects)
26
-
27
  c) Finally, integrate characters (if applicable):
28
  - Their position within the established environment
29
  - How they interact with the space
30
  - Their expressions and actions as part of the scene
31
-
32
  3) Each composition should:
33
  - Flow naturally like a single, cohesive description
34
  - Prioritize environmental storytelling
35
  - Build the scene progressively from background to foreground
36
  - Maintain consistent atmosphere throughout
 
37
 
38
  4) For NO-CHAR compositions:
39
  Focus entirely on a and b of the prompt structure, with extra emphasis on:
40
- - Environmental details and patterns
41
- - Architectural elements
42
  - Natural phenomena
43
- - Atmospheric qualities
44
 
45
- Note: Avoid jumping between environment and character descriptions. Each element should flow naturally into the next, creating a unified visual narrative.
46
  """
47
 
48
  generate_image_compositions_user_prompt = """\
49
  Here's are the details:
50
 
51
- ## Synopsis:
52
  {{FRAME}}
 
 
 
 
 
 
53
  """
 
1
  generate_image_compositions_instruction = """\
2
+ As a visual artist and cinematographer with extensive knowledge of different camera angles, visual styles, and aesthetics, your task is to analyze the provided frame details and create four distinct compositions, maintaining consistency with previous frame's location and settings. Each composition should follow a specific narrative structure in its prompt construction:
3
 
4
  1) Output Requirements in json:
5
  {
6
  "compositions": [
7
  {
8
  "prompt": "Detailed visual description following the structure below (max 77 tokens)",
9
+ "shot_type": "Optimal cinematographic shot",
10
  }
11
  ]
12
  }
13
 
14
  2) Prompt Structure (in this specific order):
15
  a) Begin with the environment and setting:
16
+ - Establish the broader landscape/location first, maintaining consistency with previous frame
17
+ - Describe key environmental elements that persist from previous frame
18
+ - Set the atmospheric conditions aligned with previous settings
19
+ - Define the lighting and mood that follows from previous frame
20
+
21
  b) Then layer in the scene elements:
22
  - How different parts of the environment interact
23
  - Spatial relationships and depth
24
+ - Textures and materials consistent with previous frame
25
  - Any dynamic elements (movement, weather effects)
26
+
27
  c) Finally, integrate characters (if applicable):
28
  - Their position within the established environment
29
  - How they interact with the space
30
  - Their expressions and actions as part of the scene
31
+
32
  3) Each composition should:
33
  - Flow naturally like a single, cohesive description
34
  - Prioritize environmental storytelling
35
  - Build the scene progressively from background to foreground
36
  - Maintain consistent atmosphere throughout
37
+ - Ensure visual continuity with previous frame's location and settings
38
 
39
  4) For NO-CHAR compositions:
40
  Focus entirely on a and b of the prompt structure, with extra emphasis on:
41
+ - Environmental details and patterns that match previous frame
42
+ - Architectural elements maintaining previous frame's style
43
  - Natural phenomena
44
+ - Atmospheric qualities aligned with previous settings
45
 
46
+ Note: Avoid jumping between environment and character descriptions. Each element should flow naturally into the next, creating a unified visual narrative while maintaining consistency with previous frame's location and settings.\
47
  """
48
 
49
  generate_image_compositions_user_prompt = """\
50
  Here's are the details:
51
 
52
+ ## Current Frame:
53
  {{FRAME}}
54
+
55
+ Previous Frame location:
56
+ {{LOCATION_DESCRIPTION}}
57
+
58
+ Previous Frame settings:
59
+ {{frame_settings}}
60
  """