chansung commited on
Commit
ceefdf5
β€’
1 Parent(s): 88c9ec9
README.md CHANGED
@@ -1,13 +1,3 @@
1
- ---
2
- title: Zero2Story
3
- emoji: πŸ“–
4
- sdk: gradio
5
- sdk_version: 3.42.0
6
- app_file: app.py
7
- pinned: false
8
- license: apache-2.0
9
- ---
10
-
11
  # Zero2Story
12
 
13
  ![](assets/overview.png)
@@ -35,7 +25,7 @@ This project heavily depends on [PaLM API](https://developers.generativeai.googl
35
  Make sure you have installed all of the following prerequisites on your development machine:
36
  * CUDA Toolkit 11.8 with cuDNN 8 - [Download & Install CUDA Toolkit](https://developer.nvidia.com/cuda-toolkit) It is highly recommended to run on a GPU. If you run it in a CPU environment, it will be very slow.
37
  * Poetry - [Download & Install Poetry](https://python-poetry.org/docs/#installation) It is the python packaging and dependency manager.
38
- * SQLite3 v3.37.2 or higher - It is required to be installed due to dependencies.
39
  - Ubuntu 22.04 and later
40
  ```shell
41
  $ sudo apt install libc6 sqlite3 libsqlite3
@@ -53,10 +43,21 @@ Make sure you have installed all of the following prerequisites on your developm
53
  ```shell
54
  $ sudo apt install ffmpeg
55
 
56
- ## Run
57
 
 
58
  ```shell
59
  $ poetry install
 
 
 
 
 
 
 
 
 
 
60
  $ poetry run python app.py
61
  ```
62
 
@@ -73,4 +74,4 @@ $ poetry run python app.py
73
 
74
  ### VAEs
75
  - For character image generation: [CIVIT.AI Model 23906](https://civitai.com/models/23906)
76
- - For background image generation: [CIVIT.AI Model 65728](https://civitai.com/models/65728)
 
 
 
 
 
 
 
 
 
 
 
1
  # Zero2Story
2
 
3
  ![](assets/overview.png)
 
25
  Make sure you have installed all of the following prerequisites on your development machine:
26
  * CUDA Toolkit 11.8 with cuDNN 8 - [Download & Install CUDA Toolkit](https://developer.nvidia.com/cuda-toolkit) It is highly recommended to run on a GPU. If you run it in a CPU environment, it will be very slow.
27
  * Poetry - [Download & Install Poetry](https://python-poetry.org/docs/#installation) It is the python packaging and dependency manager.
28
+ * SQLite3 v3.35.0 or higher - It is required to be installed due to dependencies.
29
  - Ubuntu 22.04 and later
30
  ```shell
31
  $ sudo apt install libc6 sqlite3 libsqlite3
 
43
  ```shell
44
  $ sudo apt install ffmpeg
45
 
46
+ ## Installation
47
 
48
+ Before running the application for the first time, install the required dependencies:
49
  ```shell
50
  $ poetry install
51
+ ```
52
+
53
+ If dependencies change or need updates in the future, you can use:
54
+ ```shell
55
+ $ poetry update
56
+ ```
57
+
58
+ ## Run
59
+
60
+ ```shell
61
  $ poetry run python app.py
62
  ```
63
 
 
74
 
75
  ### VAEs
76
  - For character image generation: [CIVIT.AI Model 23906](https://civitai.com/models/23906)
77
+ - For background image generation: [CIVIT.AI Model 65728](https://civitai.com/models/65728)
app.py CHANGED
@@ -10,16 +10,14 @@ from constants.init_values import (
10
  from constants import desc
11
 
12
  from interfaces import (
13
- ui, chat_ui, story_gen_ui, view_change_ui
14
  )
15
  from modules.palmchat import GradioPaLMChatPPManager
16
 
17
  with gr.Blocks(css=STYLE) as demo:
18
- chat_mode = gr.State("plot_chat")
19
-
20
  chat_state = gr.State({
21
- "ppmanager_type": GradioPaLMChatPPManager(),
22
- "plot_chat": GradioPaLMChatPPManager(),
23
  "story_chat": GradioPaLMChatPPManager(),
24
  "export_chat": GradioPaLMChatPPManager(),
25
  })
@@ -31,6 +29,11 @@ with gr.Blocks(css=STYLE) as demo:
31
  gallery_images2 = gr.State(default_character_images)
32
  gallery_images3 = gr.State(default_character_images)
33
  gallery_images4 = gr.State(default_character_images)
 
 
 
 
 
34
 
35
  with gr.Column(visible=True) as pre_phase:
36
  gr.Markdown("# πŸ“– Zero2Story", elem_classes=["markdown-center"])
@@ -242,16 +245,6 @@ with gr.Blocks(css=STYLE) as demo:
242
  "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer interdum eleifend tincidunt. Vivamus dapibus, massa ut imperdiet condimentum, quam ipsum vehicula eros, a accumsan nisl metus at nisl. Nullam tortor nibh, vehicula sed tellus at, accumsan efficitur enim. Sed mollis purus vitae nisl ornare volutpat. In vitae tortor nec neque sagittis vehicula. In vestibulum velit eu lorem pulvinar dignissim. Donec eu sapien et sapien cursus pretium elementum eu urna. Proin lacinia ipsum maximus, commodo dui tempus, convallis tortor. Nulla sodales mi libero, nec eleifend eros interdum quis. Pellentesque nulla lectus, scelerisque et consequat vitae, blandit at ante. Sed nec …….",
243
  lines=12,
244
  elem_classes=["no-label", "small-big-textarea"]
245
- )
246
-
247
- action_types = gr.Radio(
248
- choices=[
249
- "continue current phase", "move to the next phase"
250
- ],
251
- value="continue current phase",
252
- interactive=True,
253
- elem_classes=["no-label-radio"],
254
- visible=False,
255
  )
256
 
257
  with gr.Accordion("regeneration controls", open=False):
@@ -259,7 +252,7 @@ with gr.Blocks(css=STYLE) as demo:
259
  regen_actions_btn = gr.Button("Re-suggest actions", interactive=True, elem_classes=["control-button-green"])
260
  regen_story_btn = gr.Button("Re-suggest story and actions", interactive=True, elem_classes=["control-button-green"])
261
 
262
- custom_prompt_txt = gr.Textbox(placeholder="Re-suggest story and actions based on your own custom request", elem_classes=["no-label", "small-big-textarea"])
263
 
264
  with gr.Row():
265
  action_btn1 = gr.Button("Action Choice 1", interactive=False, elem_classes=["control-button-green"])
@@ -273,9 +266,23 @@ with gr.Blocks(css=STYLE) as demo:
273
  story_writing_done_btn = gr.Button("export your story β†’", elem_classes=["wrap", "control-button"], scale=2)
274
 
275
  with gr.Column(visible=False) as export_phase:
276
- gr.Markdown("### πŸ“€ Export output")
277
- with gr.Accordion("generate chapter titles and each plot", open=False) as export_section:
278
- gr.Markdown("hello")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
 
280
  with gr.Accordion("πŸ’¬", open=False, elem_id="chat-section") as chat_section:
281
  with gr.Column(scale=1):
@@ -320,6 +327,91 @@ with gr.Blocks(css=STYLE) as demo:
320
  outputs=[pre_phase, writing_phase]
321
  )
322
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  character_setup_confirm_btn.click(
324
  view_change_ui.move_to_next_view,
325
  inputs=None,
@@ -407,25 +499,25 @@ with gr.Blocks(css=STYLE) as demo:
407
  ui.gen_character_image,
408
  inputs=[
409
  gallery_images1, name_txt1, age_dd1, mbti_dd1, personality_dd1, job_dd1, genre_dd, place_dd, mood_dd, creative_dd1],
410
- outputs=[char_gallery1, gallery_images1]
411
  )
412
 
413
  gen_char_btn2.click(
414
  ui.gen_character_image,
415
  inputs=[gallery_images2, name_txt2, age_dd2, mbti_dd2, personality_dd2, job_dd2, genre_dd, place_dd, mood_dd, creative_dd2],
416
- outputs=[char_gallery2, gallery_images2]
417
  )
418
 
419
  gen_char_btn3.click(
420
  ui.gen_character_image,
421
  inputs=[gallery_images3, name_txt3, age_dd3, mbti_dd3, personality_dd3, job_dd3, genre_dd, place_dd, mood_dd, creative_dd3],
422
- outputs=[char_gallery3, gallery_images3]
423
  )
424
 
425
  gen_char_btn4.click(
426
  ui.gen_character_image,
427
  inputs=[gallery_images4, name_txt4, age_dd4, mbti_dd4, personality_dd4, job_dd4, genre_dd, place_dd, mood_dd, creative_dd4],
428
- outputs=[char_gallery4, gallery_images4]
429
  )
430
 
431
  random_name_btn1.click(
@@ -685,4 +777,28 @@ with gr.Blocks(css=STYLE) as demo:
685
  outputs=[chat_input_txt, chat_state, chatbot, regen_btn]
686
  )
687
 
688
- demo.queue().launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  from constants import desc
11
 
12
  from interfaces import (
13
+ ui, chat_ui, story_gen_ui, view_change_ui, export_ui
14
  )
15
  from modules.palmchat import GradioPaLMChatPPManager
16
 
17
  with gr.Blocks(css=STYLE) as demo:
18
+ chat_mode = gr.State("setting_chat")
 
19
  chat_state = gr.State({
20
+ "setting_chat": GradioPaLMChatPPManager(),
 
21
  "story_chat": GradioPaLMChatPPManager(),
22
  "export_chat": GradioPaLMChatPPManager(),
23
  })
 
29
  gallery_images2 = gr.State(default_character_images)
30
  gallery_images3 = gr.State(default_character_images)
31
  gallery_images4 = gr.State(default_character_images)
32
+
33
+ selected_main_char_image1 = gr.State(default_character_images[0])
34
+ selected_side_char_image1 = gr.State(default_character_images[0])
35
+ selected_side_char_image2 = gr.State(default_character_images[0])
36
+ selected_side_char_image3 = gr.State(default_character_images[0])
37
 
38
  with gr.Column(visible=True) as pre_phase:
39
  gr.Markdown("# πŸ“– Zero2Story", elem_classes=["markdown-center"])
 
245
  "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer interdum eleifend tincidunt. Vivamus dapibus, massa ut imperdiet condimentum, quam ipsum vehicula eros, a accumsan nisl metus at nisl. Nullam tortor nibh, vehicula sed tellus at, accumsan efficitur enim. Sed mollis purus vitae nisl ornare volutpat. In vitae tortor nec neque sagittis vehicula. In vestibulum velit eu lorem pulvinar dignissim. Donec eu sapien et sapien cursus pretium elementum eu urna. Proin lacinia ipsum maximus, commodo dui tempus, convallis tortor. Nulla sodales mi libero, nec eleifend eros interdum quis. Pellentesque nulla lectus, scelerisque et consequat vitae, blandit at ante. Sed nec …….",
246
  lines=12,
247
  elem_classes=["no-label", "small-big-textarea"]
 
 
 
 
 
 
 
 
 
 
248
  )
249
 
250
  with gr.Accordion("regeneration controls", open=False):
 
252
  regen_actions_btn = gr.Button("Re-suggest actions", interactive=True, elem_classes=["control-button-green"])
253
  regen_story_btn = gr.Button("Re-suggest story and actions", interactive=True, elem_classes=["control-button-green"])
254
 
255
+ custom_prompt_txt = gr.Textbox(placeholder="Re-suggest story and actions based on your own custom request", interactive=True, elem_classes=["no-label", "small-big-textarea"])
256
 
257
  with gr.Row():
258
  action_btn1 = gr.Button("Action Choice 1", interactive=False, elem_classes=["control-button-green"])
 
266
  story_writing_done_btn = gr.Button("export your story β†’", elem_classes=["wrap", "control-button"], scale=2)
267
 
268
  with gr.Column(visible=False) as export_phase:
269
+ gr.Markdown("# πŸ“€ Story writing")
270
+ gr.Markdown(desc.export_phase_description, elem_classes=["markdown-justify"])
271
+
272
+ title_txt = gr.Textbox("Your Own Story", elem_classes=["no-label"])
273
+ title_gen_btn = gr.Button("gnerate a title", elem_classes=["control-button-green"])
274
+
275
+ with gr.Row():
276
+ back_to_story_writing_btn = gr.Button("← back", elem_classes=["wrap", "control-button"], scale=1)
277
+ restart_from_export_btn = gr.Button("start over", elem_classes=["wrap", "control-button"], scale=1)
278
+ export_done_btn = gr.Button("exported story β†’", elem_classes=["wrap", "control-button"], scale=1)
279
+
280
+ with gr.Column(visible=False) as export_view_phase:
281
+ export_html = gr.HTML()
282
+
283
+ with gr.Row():
284
+ restart_from_export_view_btn = gr.Button("start over", elem_classes=["wrap", "control-button"])
285
+ export_to_file_btn = gr.Button("Download as ZIP file", elem_classes=["wrap", "control-button"], scale=2)
286
 
287
  with gr.Accordion("πŸ’¬", open=False, elem_id="chat-section") as chat_section:
288
  with gr.Column(scale=1):
 
327
  outputs=[pre_phase, writing_phase]
328
  )
329
 
330
+ story_writing_done_btn.click(
331
+ view_change_ui.move_to_next_view,
332
+ inputs=None,
333
+ outputs=[writing_phase, export_phase]
334
+ )
335
+
336
+ title_gen_btn.click(
337
+ export_ui.title_gen,
338
+ inputs=[cursors],
339
+ outputs=[title_txt]
340
+ )
341
+
342
+ export_done_btn.click(
343
+ view_change_ui.move_to_next_view,
344
+ inputs=None,
345
+ outputs=[export_phase, export_view_phase]
346
+ ).then(
347
+ export_ui.export,
348
+ inputs=[
349
+ title_txt,
350
+ cursors,
351
+ selected_main_char_image1, name_txt1, age_dd1, mbti_dd1, personality_dd1, job_dd1,
352
+ side_char_enable_ckb1, selected_side_char_image1, name_txt2, age_dd2, mbti_dd2, personality_dd2, job_dd2,
353
+ side_char_enable_ckb2, selected_side_char_image2, name_txt3, age_dd3, mbti_dd3, personality_dd3, job_dd3,
354
+ side_char_enable_ckb3, selected_side_char_image3, name_txt4, age_dd4, mbti_dd4, personality_dd4, job_dd4,
355
+ ],
356
+ outputs=[
357
+ export_html
358
+ ]
359
+ )
360
+
361
+ back_to_story_writing_btn.click(
362
+ view_change_ui.back_to_previous_view,
363
+ inputs=None,
364
+ outputs=[writing_phase, export_phase]
365
+ )
366
+
367
+ restart_from_export_view_btn.click(
368
+ view_change_ui.back_to_previous_view,
369
+ inputs=None,
370
+ outputs=[pre_phase, export_view_phase]
371
+ ).then(
372
+ ui.reset,
373
+ inputs=None,
374
+ outputs=[
375
+ cursors, cur_cursor,
376
+ chat_state, chat_mode,
377
+ gallery_images1, gallery_images2, gallery_images3, gallery_images4,
378
+ selected_main_char_image1, selected_side_char_image1, selected_side_char_image2, selected_side_char_image3,
379
+ genre_dd, place_dd, mood_dd,
380
+ char_gallery1, job_dd1,
381
+ side_char_enable_ckb1, char_gallery2, job_dd2,
382
+ side_char_enable_ckb2, char_gallery3, job_dd3,
383
+ side_char_enable_ckb3, char_gallery4, job_dd4,
384
+ story_image, story_audio, story_video,
385
+ story_content, story_progress,
386
+ custom_prompt_txt, action_btn1, action_btn2, action_btn3, custom_action_txt,
387
+ title_txt, export_html
388
+ ]
389
+ )
390
+
391
+ restart_from_export_btn.click(
392
+ view_change_ui.back_to_previous_view,
393
+ inputs=None,
394
+ outputs=[pre_phase, export_phase]
395
+ ).then(
396
+ ui.reset,
397
+ inputs=None,
398
+ outputs=[
399
+ cursors, cur_cursor,
400
+ chat_state, chat_mode,
401
+ gallery_images1, gallery_images2, gallery_images3, gallery_images4,
402
+ selected_main_char_image1, selected_side_char_image1, selected_side_char_image2, selected_side_char_image3,
403
+ genre_dd, place_dd, mood_dd,
404
+ char_gallery1, job_dd1,
405
+ side_char_enable_ckb1, char_gallery2, job_dd2,
406
+ side_char_enable_ckb2, char_gallery3, job_dd3,
407
+ side_char_enable_ckb3, char_gallery4, job_dd4,
408
+ story_image, story_audio, story_video,
409
+ story_content, story_progress,
410
+ custom_prompt_txt, action_btn1, action_btn2, action_btn3, custom_action_txt,
411
+ title_txt, export_html
412
+ ]
413
+ )
414
+
415
  character_setup_confirm_btn.click(
416
  view_change_ui.move_to_next_view,
417
  inputs=None,
 
499
  ui.gen_character_image,
500
  inputs=[
501
  gallery_images1, name_txt1, age_dd1, mbti_dd1, personality_dd1, job_dd1, genre_dd, place_dd, mood_dd, creative_dd1],
502
+ outputs=[char_gallery1, gallery_images1, selected_main_char_image1]
503
  )
504
 
505
  gen_char_btn2.click(
506
  ui.gen_character_image,
507
  inputs=[gallery_images2, name_txt2, age_dd2, mbti_dd2, personality_dd2, job_dd2, genre_dd, place_dd, mood_dd, creative_dd2],
508
+ outputs=[char_gallery2, gallery_images2, selected_side_char_image1]
509
  )
510
 
511
  gen_char_btn3.click(
512
  ui.gen_character_image,
513
  inputs=[gallery_images3, name_txt3, age_dd3, mbti_dd3, personality_dd3, job_dd3, genre_dd, place_dd, mood_dd, creative_dd3],
514
+ outputs=[char_gallery3, gallery_images3, selected_side_char_image2]
515
  )
516
 
517
  gen_char_btn4.click(
518
  ui.gen_character_image,
519
  inputs=[gallery_images4, name_txt4, age_dd4, mbti_dd4, personality_dd4, job_dd4, genre_dd, place_dd, mood_dd, creative_dd4],
520
+ outputs=[char_gallery4, gallery_images4, selected_side_char_image3]
521
  )
522
 
523
  random_name_btn1.click(
 
777
  outputs=[chat_input_txt, chat_state, chatbot, regen_btn]
778
  )
779
 
780
+ char_gallery1.select(
781
+ ui.update_selected_char_image,
782
+ inputs=None,
783
+ outputs=[selected_main_char_image1]
784
+ )
785
+
786
+ char_gallery2.select(
787
+ ui.update_selected_char_image,
788
+ inputs=None,
789
+ outputs=[selected_side_char_image1]
790
+ )
791
+
792
+ char_gallery3.select(
793
+ ui.update_selected_char_image,
794
+ inputs=None,
795
+ outputs=[selected_side_char_image2]
796
+ )
797
+
798
+ char_gallery4.select(
799
+ ui.update_selected_char_image,
800
+ inputs=None,
801
+ outputs=[selected_side_char_image3]
802
+ )
803
+
804
+ demo.queue().launch(share=True)
assets/overview.png CHANGED

Git LFS Details

  • SHA256: 15d5acbd4593eee43653544e2365cc81ac6ada2ab250c9d30660a95bee70687d
  • Pointer size: 132 Bytes
  • Size of remote file: 2.67 MB

Git LFS Details

  • SHA256: aa30d05aca6aeb694437a3651e0bff2e7886c281743cab6cc8042c4c78b12381
  • Pointer size: 131 Bytes
  • Size of remote file: 973 kB
assets/palm_prompts.toml CHANGED
@@ -12,7 +12,7 @@ To enhance the quality of your character's description or expression, you might
12
  - Age and Gender: "1boy", "1man", "1male", "1girl", "1woman", "1female", "teen", "teenage", "twenties", "thirties", "forties", "fifties", "middle-age".
13
  Do note that this list isn't exhaustive, and you're encouraged to suggest similar terms not included here.
14
  Exclude words from the suggestion that are redundant or have conflicting meanings.
15
- Especially, Exclude words that conflict with the meaning of "main_sentence".
16
  Do not output anything other than JSON values.
17
  Do not provide any additional explanation of the following.
18
  Only JSON is allowed.
@@ -64,7 +64,7 @@ To enhance the quality of your scene's description or expression, you might cons
64
  - Visual Enhancements: "masterpiece", "cinematic lighting", "detailed lighting", "soft lighting", "volumetric lighting", "tyndall effect", "warm lighting", "close up", "wide shot", "beautiful perspective", "bokeh".
65
  Do note that this list isn't exhaustive, and you're encouraged to suggest similar terms not included here.
66
  Exclude words from the suggestion that are redundant or have conflicting meanings.
67
- Especially, Exclude words that conflict with the meaning of "main_sentence".
68
  Do not output anything other than JSON values.
69
  Do not provide any additional explanation of the following.
70
  Only JSON is allowed.
@@ -74,25 +74,25 @@ Q:
74
  The genre is "Fantasy", the place is "Enchanted Forest", the mood is "Mystical", the title of the novel is "Whispering Leaves", and the chapter plot revolves around "A hidden glade where elves sing under the moonlight".
75
  Print out no more than 45 words in syntactically valid JSON format.
76
  A:
77
- {{"main_sentence":"a mystical glade in an enchanted forest where elves sing beneath the moonlight","descriptors":["no humans","masterpiece","fantasy","enchanted forest","moonlit glade","mystical atmosphere","singing elves","luminous fireflies","ancient trees","shimmering leaves","whispering winds","hidden secrets","elven magic","masterpiece","soft lighting","silver glow","detailed shadows","enchanted mood","highly detailed","film grain"]}}
78
 
79
  Q:
80
  The genre is "Science Fiction", the place is "Galactic Space Station", the mood is "Tense", the title of the novel is "Stars Unbound", and the chapter plot revolves around "Ambassadors from different galaxies discussing a new treaty".
81
  Print out no more than 45 words in syntactically valid JSON format.
82
  A:
83
- {{"main_sentence":"a tense gathering in a galactic space station where interstellar ambassadors negotiate","descriptors":["no humans","masterpiece","science fiction","galactic space station","star-studded backdrop","advanced technology","diverse aliens","hovering spacecrafts","futuristic architecture","tense discussions","interstellar politics","neon lights","holographic displays","masterpiece","detailed lighting","cinematic mood","highly detailed","film grain"]}}
84
 
85
  Q:
86
  The genre is "Romance", the place is "Beach", the mood is "Heartfelt", the title of the novel is "Waves of Passion", and the chapter plot revolves around "Two lovers reconciling their differences by the shore".
87
  Print out no more than 45 words in syntactically valid JSON format.
88
  A:
89
- {{"main_sentence":"a heartfelt scene on a beach during sunset where two lovers reconcile","descriptors":["no humans","masterpiece","romance","beach","sunset horizon","golden sands","lapping waves","embrace","teary-eyed confessions","seashells","reflective waters","warm hues","silhouette of lovers","soft breeze","beautiful perspective","detailed shadows","emotional atmosphere","highly detailed","film grain"]}}
90
 
91
  Q:
92
  The genre is "Middle Ages", the place is "Royal Palace", the mood is "Epic Adventure", the title of the novel is "Throne of Fates", and the chapter plot revolves around "A brave knight receiving a quest from the king".
93
  Print out no more than 45 words in syntactically valid JSON format.
94
  A:
95
- {{"main_sentence":"an epic scene in a royal palace where a knight is tasked with a quest by the king","descriptors":["no humans","masterpiece","middle ages","royal palace","castle","grand throne room","golden hour","armored knight","majestic king","tapestries","stone walls","torches","glistening armor","banner flags","medieval atmosphere","heroic demeanor","detailed architecture","golden crowns","highly detailed","film grain"]}}
96
  ===
97
  This is my request.
98
  Q:
@@ -126,22 +126,22 @@ This is some examples.
126
  Q:
127
  The genre is "Fantasy", the place is "Enchanted Forest", the mood is "Mystical", the title of the novel is "Whispering Leaves", and the chapter plot revolves around "A hidden glade where elves sing under the moonlight".
128
  A:
129
- {{"main_sentence":"a gentle folk melody filled with whimsical flutes, echoing harps, and distant ethereal vocals, capturing the enchantment of a moonlit forest and the mystique of singing elves"}}
130
 
131
  Q:
132
  The genre is "Science Fiction", the place is "Galactic Space Station", the mood is "Tense", the title of the novel is "Stars Unbound", and the chapter plot revolves around "Ambassadors from different galaxies discussing a new treaty".
133
  A:
134
- {{"main_sentence":"an ambient electronic track, with pulsating synths, spacey reverberations, and occasional digital glitches, reflecting the vastness of space and the tension of intergalactic diplomacy"}}
135
 
136
  Q:
137
  The genre is "Romance", the place is "Beach", the mood is "Heartfelt", the title of the novel is "Waves of Passion", and the chapter plot revolves around "Two lovers reconciling their differences by the shore".
138
  A:
139
- {{"main_sentence":"a soft acoustic ballad featuring soulful guitars, delicate percussion, and heartfelt vocals, evoking feelings of love, reconciliation, and the gentle ebb and flow of the ocean waves"}}
140
 
141
  Q:
142
  The genre is "Middle Ages", the place is "Royal Palace", the mood is "Epic Adventure", the title of the novel is "Throne of Fates", and the chapter plot revolves around "A brave knight receiving a quest from the king".
143
  A:
144
- {{"main_sentence":"a grand orchestral piece, dominated by powerful brass, rhythmic drums, and soaring strings, portraying the valor of knights, the majesty of royalty, and the anticipation of an epic quest"}}
145
  ===
146
  This is my request.
147
  Q:
@@ -150,5 +150,5 @@ A:
150
  """
151
  query = """
152
  The genre is "{genre}", the place is "{place}", the mood is "{mood}", the title of the novel is "{title}", and the chapter plot revolves around "{chapter_plot}".
153
- Print out only one main_sentence in syntactically valid JSON format.
154
  """
 
12
  - Age and Gender: "1boy", "1man", "1male", "1girl", "1woman", "1female", "teen", "teenage", "twenties", "thirties", "forties", "fifties", "middle-age".
13
  Do note that this list isn't exhaustive, and you're encouraged to suggest similar terms not included here.
14
  Exclude words from the suggestion that are redundant or have conflicting meanings.
15
+ Especially, Exclude words that conflict with the meaning of "primary_sentence".
16
  Do not output anything other than JSON values.
17
  Do not provide any additional explanation of the following.
18
  Only JSON is allowed.
 
64
  - Visual Enhancements: "masterpiece", "cinematic lighting", "detailed lighting", "soft lighting", "volumetric lighting", "tyndall effect", "warm lighting", "close up", "wide shot", "beautiful perspective", "bokeh".
65
  Do note that this list isn't exhaustive, and you're encouraged to suggest similar terms not included here.
66
  Exclude words from the suggestion that are redundant or have conflicting meanings.
67
+ Especially, Exclude words that conflict with the meaning of "primary_sentence".
68
  Do not output anything other than JSON values.
69
  Do not provide any additional explanation of the following.
70
  Only JSON is allowed.
 
74
  The genre is "Fantasy", the place is "Enchanted Forest", the mood is "Mystical", the title of the novel is "Whispering Leaves", and the chapter plot revolves around "A hidden glade where elves sing under the moonlight".
75
  Print out no more than 45 words in syntactically valid JSON format.
76
  A:
77
+ {{"primary_sentence":"a mystical glade in an enchanted forest where elves sing beneath the moonlight","descriptors":["no humans","masterpiece","fantasy","enchanted forest","moonlit glade","mystical atmosphere","singing elves","luminous fireflies","ancient trees","shimmering leaves","whispering winds","hidden secrets","elven magic","masterpiece","soft lighting","silver glow","detailed shadows","enchanted mood","highly detailed","film grain"]}}
78
 
79
  Q:
80
  The genre is "Science Fiction", the place is "Galactic Space Station", the mood is "Tense", the title of the novel is "Stars Unbound", and the chapter plot revolves around "Ambassadors from different galaxies discussing a new treaty".
81
  Print out no more than 45 words in syntactically valid JSON format.
82
  A:
83
+ {{"primary_sentence":"a tense gathering in a galactic space station where interstellar ambassadors negotiate","descriptors":["no humans","masterpiece","science fiction","galactic space station","star-studded backdrop","advanced technology","diverse aliens","hovering spacecrafts","futuristic architecture","tense discussions","interstellar politics","neon lights","holographic displays","masterpiece","detailed lighting","cinematic mood","highly detailed","film grain"]}}
84
 
85
  Q:
86
  The genre is "Romance", the place is "Beach", the mood is "Heartfelt", the title of the novel is "Waves of Passion", and the chapter plot revolves around "Two lovers reconciling their differences by the shore".
87
  Print out no more than 45 words in syntactically valid JSON format.
88
  A:
89
+ {{"primary_sentence":"a heartfelt scene on a beach during sunset where two lovers reconcile","descriptors":["no humans","masterpiece","romance","beach","sunset horizon","golden sands","lapping waves","embrace","teary-eyed confessions","seashells","reflective waters","warm hues","silhouette of lovers","soft breeze","beautiful perspective","detailed shadows","emotional atmosphere","highly detailed","film grain"]}}
90
 
91
  Q:
92
  The genre is "Middle Ages", the place is "Royal Palace", the mood is "Epic Adventure", the title of the novel is "Throne of Fates", and the chapter plot revolves around "A brave knight receiving a quest from the king".
93
  Print out no more than 45 words in syntactically valid JSON format.
94
  A:
95
+ {{"primary_sentence":"an epic scene in a royal palace where a knight is tasked with a quest by the king","descriptors":["no humans","masterpiece","middle ages","royal palace","castle","grand throne room","golden hour","armored knight","majestic king","tapestries","stone walls","torches","glistening armor","banner flags","medieval atmosphere","heroic demeanor","detailed architecture","golden crowns","highly detailed","film grain"]}}
96
  ===
97
  This is my request.
98
  Q:
 
126
  Q:
127
  The genre is "Fantasy", the place is "Enchanted Forest", the mood is "Mystical", the title of the novel is "Whispering Leaves", and the chapter plot revolves around "A hidden glade where elves sing under the moonlight".
128
  A:
129
+ {{"primary_sentence":"a gentle folk melody filled with whimsical flutes, echoing harps, and distant ethereal vocals, capturing the enchantment of a moonlit forest and the mystique of singing elves"}}
130
 
131
  Q:
132
  The genre is "Science Fiction", the place is "Galactic Space Station", the mood is "Tense", the title of the novel is "Stars Unbound", and the chapter plot revolves around "Ambassadors from different galaxies discussing a new treaty".
133
  A:
134
+ {{"primary_sentence":"an ambient electronic track, with pulsating synths, spacey reverberations, and occasional digital glitches, reflecting the vastness of space and the tension of intergalactic diplomacy"}}
135
 
136
  Q:
137
  The genre is "Romance", the place is "Beach", the mood is "Heartfelt", the title of the novel is "Waves of Passion", and the chapter plot revolves around "Two lovers reconciling their differences by the shore".
138
  A:
139
+ {{"primary_sentence":"a soft acoustic ballad featuring soulful guitars, delicate percussion, and heartfelt vocals, evoking feelings of love, reconciliation, and the gentle ebb and flow of the ocean waves"}}
140
 
141
  Q:
142
  The genre is "Middle Ages", the place is "Royal Palace", the mood is "Epic Adventure", the title of the novel is "Throne of Fates", and the chapter plot revolves around "A brave knight receiving a quest from the king".
143
  A:
144
+ {{"primary_sentence":"a grand orchestral piece, dominated by powerful brass, rhythmic drums, and soaring strings, portraying the valor of knights, the majesty of royalty, and the anticipation of an epic quest"}}
145
  ===
146
  This is my request.
147
  Q:
 
150
  """
151
  query = """
152
  The genre is "{genre}", the place is "{place}", the mood is "{mood}", the title of the novel is "{title}", and the chapter plot revolves around "{chapter_plot}".
153
+ Print out only one primary_sentence in syntactically valid JSON format.
154
  """
constants/desc.py CHANGED
@@ -16,4 +16,7 @@ story_generation_phase_description = """
16
  In this phase, the first few paragraphs are generated solely based on the information from the background and character setup phases. Afterwards, users could choose a direction from the given three options that PaLM API generated. Then, further stories are generated based on users' choice. This cycle of choosing an option and generating further stories are interatively continued until users decides to stop.
17
 
18
  In each story generation, users also could generate background images and music that describe each scene using Stable Diffusion and MusicGen. If the generated story, options, image, and music in each turn, users could ask to re-generate them.
 
 
 
19
  """
 
16
  In this phase, the first few paragraphs are generated solely based on the information from the background and character setup phases. Afterwards, users could choose a direction from the given three options that PaLM API generated. Then, further stories are generated based on users' choice. This cycle of choosing an option and generating further stories are interatively continued until users decides to stop.
17
 
18
  In each story generation, users also could generate background images and music that describe each scene using Stable Diffusion and MusicGen. If the generated story, options, image, and music in each turn, users could ask to re-generate them.
19
+ """
20
+ export_phase_description = """
21
+ In this phase, you can export the generated whole stuffs including text, image, audio, and video in the form of a static HTML page.
22
  """
interfaces/export_ui.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from templates import parser
3
+ from interfaces import utils
4
+ from modules import palmchat
5
+
6
+ template_file = "templates/basic.jinja"
7
+
8
+ async def title_gen(cursors):
9
+ stories = ""
10
+ for cursor in cursors:
11
+ stories = stories + cursor["story"]
12
+
13
+ prompt = f"""what would be the title of the story below? be specific and creative.
14
+
15
+ {stories}
16
+
17
+ title: """
18
+
19
+ parameters = {
20
+ 'model': 'models/text-bison-001',
21
+ 'candidate_count': 1,
22
+ 'temperature': 0.7,
23
+ 'top_k': 40,
24
+ 'top_p': 1,
25
+ 'max_output_tokens': 4096,
26
+ }
27
+ _, title = await palmchat.gen_text(prompt, mode="text", parameters=parameters)
28
+ return title
29
+
30
+ def export(
31
+ title, cursors,
32
+ main_char_img, main_char_name, main_char_age, main_char_mbti, main_char_personality, main_char_job,
33
+ side_char_enable1, side_char_img1, side_char_name1, side_char_age1, side_char_mbti1, side_char_personality1, side_char_job1,
34
+ side_char_enable2, side_char_img2, side_char_name2, side_char_age2, side_char_mbti2, side_char_personality2, side_char_job2,
35
+ side_char_enable3, side_char_img3, side_char_name3, side_char_age3, side_char_mbti3, side_char_personality3, side_char_job3,
36
+ ):
37
+ print(main_char_img)
38
+ characters = [
39
+ {
40
+ 'img': main_char_img,
41
+ 'name': main_char_name,
42
+ }
43
+ ]
44
+ utils.add_side_character_to_export(
45
+ characters, side_char_enable1, side_char_img1, side_char_name1, side_char_age1, side_char_mbti1, side_char_personality1, side_char_job1
46
+ )
47
+ utils.add_side_character_to_export(
48
+ characters, side_char_enable2, side_char_img2, side_char_name2, side_char_age2, side_char_mbti2, side_char_personality2, side_char_job2
49
+ )
50
+ utils.add_side_character_to_export(
51
+ characters, side_char_enable3, side_char_img3, side_char_name3, side_char_age3, side_char_mbti3, side_char_personality3, side_char_job3
52
+ )
53
+
54
+ html_as_string = parser.gen_from_file(
55
+ template_file,
56
+ kwargs={
57
+ "title": title,
58
+ "characters": characters,
59
+ "items": cursors
60
+ }
61
+ )
62
+
63
+ return html_as_string
interfaces/story_gen_ui.py CHANGED
@@ -14,10 +14,11 @@ from pingpong import PingPong
14
  from pingpong.context import CtxLastWindowStrategy
15
 
16
  # TODO: Replace checkpoint filename to Huggingface URL
17
- img_maker = ImageMaker('landscapeAnimePro_v20Inspiration.safetensors', vae="cute20vae.safetensors")
 
18
  #img_maker = ImageMaker('fantasyworldFp16.safetensors', vae="cute20vae.safetensors")
19
  #img_maker = ImageMaker('forgesagalandscapemi.safetensors', vae="anythingFp16.safetensors")
20
- bgm_maker = None#MusicMaker(model_size='large', output_format='mp3')
21
 
22
  video_gen_client_url = "https://0447df3cf5f7c49c46.gradio.live"
23
 
 
14
  from pingpong.context import CtxLastWindowStrategy
15
 
16
  # TODO: Replace checkpoint filename to Huggingface URL
17
+ img_maker = ImageMaker('landscapeAnimePro_v20Inspiration.safetensors') # without VAE
18
+ #img_maker = ImageMaker('landscapeAnimePro_v20Inspiration.safetensors', vae="stabilityai/sd-vae-ft-mse")
19
  #img_maker = ImageMaker('fantasyworldFp16.safetensors', vae="cute20vae.safetensors")
20
  #img_maker = ImageMaker('forgesagalandscapemi.safetensors', vae="anythingFp16.safetensors")
21
+ bgm_maker = MusicMaker(model_size='medium', output_format='mp3')
22
 
23
  video_gen_client_url = "https://0447df3cf5f7c49c46.gradio.live"
24
 
interfaces/ui.py CHANGED
@@ -7,7 +7,7 @@ import PIL
7
  from pathlib import Path
8
 
9
  from constants.init_values import (
10
- places, moods, jobs, random_names, default_character_images
11
  )
12
 
13
  from modules import (
@@ -23,6 +23,9 @@ img_maker = ImageMaker('hellonijicute25d_V10b.safetensors') # without_VAE
23
  ############
24
  # for plotting
25
 
 
 
 
26
  def get_random_name(cur_char_name, char_name1, char_name2, char_name3):
27
  tmp_random_names = copy.deepcopy(random_names)
28
  tmp_random_names.remove(cur_char_name)
@@ -58,10 +61,10 @@ def gen_character_image(
58
  img_filename = str(Path('.') / 'assets' / 'nsfw_warning.png')
59
 
60
  # update gallery
61
- gen_image = numpy.asarray(PIL.Image.open(img_filename))
62
- gallery_images.insert(0, gen_image)
63
 
64
- return gr.update(value=gallery_images), gallery_images
65
 
66
 
67
  def update_on_age(evt: gr.SelectData):
@@ -71,9 +74,9 @@ def update_on_age(evt: gr.SelectData):
71
  gr.update(value=places[evt.value][0], choices=places[evt.value]),
72
  gr.update(value=moods[evt.value][0], choices=moods[evt.value]),
73
  gr.update(value=job_list[0], choices=job_list),
74
- gr.update(value=job_list[0], choices=job_list),
75
- gr.update(value=job_list[0], choices=job_list),
76
- gr.update(value=job_list[0], choices=job_list)
77
  )
78
 
79
  ############
@@ -91,3 +94,67 @@ def update_on_main_tabs(chat_state, evt: gr.SelectData):
91
 
92
  ppm = chat_state[chat_mode]
93
  return chat_mode, ppm.build_uis()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  from pathlib import Path
8
 
9
  from constants.init_values import (
10
+ genres, places, moods, jobs, random_names, default_character_images
11
  )
12
 
13
  from modules import (
 
23
  ############
24
  # for plotting
25
 
26
+ def update_selected_char_image(evt: gr.EventData):
27
+ return evt.value
28
+
29
  def get_random_name(cur_char_name, char_name1, char_name2, char_name3):
30
  tmp_random_names = copy.deepcopy(random_names)
31
  tmp_random_names.remove(cur_char_name)
 
61
  img_filename = str(Path('.') / 'assets' / 'nsfw_warning.png')
62
 
63
  # update gallery
64
+ # gen_image = numpy.asarray(PIL.Image.open(img_filename))
65
+ gallery_images.insert(0, img_filename)
66
 
67
+ return gr.update(value=gallery_images), gallery_images, img_filename
68
 
69
 
70
  def update_on_age(evt: gr.SelectData):
 
74
  gr.update(value=places[evt.value][0], choices=places[evt.value]),
75
  gr.update(value=moods[evt.value][0], choices=moods[evt.value]),
76
  gr.update(value=job_list[0], choices=job_list),
77
+ gr.update(value=job_list[1], choices=job_list),
78
+ gr.update(value=job_list[2], choices=job_list),
79
+ gr.update(value=job_list[3], choices=job_list)
80
  )
81
 
82
  ############
 
94
 
95
  ppm = chat_state[chat_mode]
96
  return chat_mode, ppm.build_uis()
97
+
98
+ def reset():
99
+ from modules.palmchat import GradioPaLMChatPPManager
100
+
101
+ return (
102
+ [], # cursors
103
+ 0, # cur_cursor
104
+
105
+ {
106
+ "setting_chat": GradioPaLMChatPPManager(),
107
+ "story_chat": GradioPaLMChatPPManager(),
108
+ "export_chat": GradioPaLMChatPPManager(),
109
+ }, # chat_state
110
+ "setting_chat", # chat_mode
111
+
112
+ default_character_images, # gallery_images1
113
+ default_character_images, # gallery_images2
114
+ default_character_images, # gallery_images3
115
+ default_character_images, # gallery_images4
116
+ default_character_images[0], # selected_main_char_image1
117
+ default_character_images[0], # selected_side_char_image1
118
+ default_character_images[0], # selected_side_char_image2
119
+ default_character_images[0], # selected_side_char_image3
120
+
121
+ genres[0], # genre_dd
122
+ places[genres[0]][0], # place_dd
123
+ moods[genres[0]][0], # mood_dd
124
+
125
+ default_character_images, # char_gallery1
126
+ jobs[genres[0]][0], # job_dd1
127
+
128
+ False, # side_char_enable_ckb1
129
+ default_character_images, # char_gallery2
130
+ jobs[genres[0]][1], # job_dd2
131
+
132
+ False, # side_char_enable_ckb2
133
+ default_character_images, # char_gallery3
134
+ jobs[genres[0]][2], # job_dd3
135
+
136
+ False, # side_char_enable_ckb3
137
+ default_character_images, # char_gallery4
138
+ jobs[genres[0]][3], # job_dd4
139
+
140
+ None, # story_image
141
+ None, # story_audio
142
+ None, # story_video
143
+
144
+ '', # story_content
145
+ gr.Slider(
146
+ 1, 2, 1, step=1, interactive=True,
147
+ label="1/2", visible=False
148
+ ), # story_progress
149
+
150
+ '', # custom_prompt_txt
151
+
152
+ 'Action 1', # action_btn1
153
+ 'Action 2', # action_btn2
154
+ 'Action 3', # action_btn3
155
+ '', # custom_action_txt
156
+
157
+ 'Your Own Story', # title_txt
158
+
159
+ "", # export_html
160
+ )
interfaces/utils.py CHANGED
@@ -6,6 +6,20 @@ import random
6
  from modules import palmchat
7
  from pingpong.context import CtxLastWindowStrategy
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  def add_side_character(
10
  enable, prompt, cur_side_chars,
11
  name, age, mbti, personality, job
 
6
  from modules import palmchat
7
  from pingpong.context import CtxLastWindowStrategy
8
 
9
+ def add_side_character_to_export(
10
+ characters, enable, img,
11
+ name, age, mbti, personality, job
12
+ ):
13
+ if enable:
14
+ characters.append(
15
+ {
16
+ 'img': img,
17
+ 'name': name
18
+ }
19
+ )
20
+
21
+ return characters
22
+
23
  def add_side_character(
24
  enable, prompt, cur_side_chars,
25
  name, age, mbti, personality, job
modules/image_maker.py CHANGED
@@ -72,8 +72,9 @@ class ImageMaker:
72
 
73
  print("Loading the Stable Diffusion model into memory...")
74
  self.__sd_model = StableDiffusionPipeline.from_single_file(self.model_base,
75
- #torch_dtype=torch.float16,
76
- use_safetensors=True)
 
77
 
78
  # Clip Skip
79
  self.__sd_model.text_encoder.text_model.encoder.layers = self.__sd_model.text_encoder.text_model.encoder.layers[:12 - (self.clip_skip - 1)]
@@ -88,7 +89,7 @@ class ImageMaker:
88
 
89
  # VAE
90
  if self.vae:
91
- vae_model = AutoencoderKL.from_single_file(self.vae)
92
  self.__sd_model.vae = vae_model
93
 
94
  if not self.safety:
@@ -243,7 +244,7 @@ class ImageMaker:
243
 
244
  try:
245
  res_json = json.loads(response_txt)
246
- positive = (res_json['main_sentence'] if not positive else f"{positive}, {res_json['main_sentence']}") + ", "
247
  positive += ', '.join(res_json['descriptors'])
248
  except:
249
  print("=== PaLM Response ===")
 
72
 
73
  print("Loading the Stable Diffusion model into memory...")
74
  self.__sd_model = StableDiffusionPipeline.from_single_file(self.model_base,
75
+ torch_dtype=torch.float16,
76
+ use_safetensors=True,
77
+ )
78
 
79
  # Clip Skip
80
  self.__sd_model.text_encoder.text_model.encoder.layers = self.__sd_model.text_encoder.text_model.encoder.layers[:12 - (self.clip_skip - 1)]
 
89
 
90
  # VAE
91
  if self.vae:
92
+ vae_model = AutoencoderKL.from_pretrained(self.vae, torch_dtype=torch.float16)
93
  self.__sd_model.vae = vae_model
94
 
95
  if not self.safety:
 
244
 
245
  try:
246
  res_json = json.loads(response_txt)
247
+ positive = (res_json['primary_sentence'] if not positive else f"{positive}, {res_json['primary_sentence']}") + ", "
248
  positive += ', '.join(res_json['descriptors'])
249
  except:
250
  print("=== PaLM Response ===")
modules/music_maker.py CHANGED
@@ -134,7 +134,7 @@ class MusicMaker:
134
  print("=== PaLM Response ===")
135
  raise ValueError("The response from PaLM API is not in the expected format.")
136
 
137
- return res_json['main_sentence']
138
 
139
 
140
  @property
 
134
  print("=== PaLM Response ===")
135
  raise ValueError("The response from PaLM API is not in the expected format.")
136
 
137
+ return res_json['primary_sentence']
138
 
139
 
140
  @property
pyproject.toml CHANGED
@@ -23,6 +23,7 @@ bingbong = "^0.4.2"
23
  asyncio = "^3.4.3"
24
  toml = "^0.10.2"
25
  compel = "^2.0.2"
 
26
 
27
  [[tool.poetry.source]]
28
  name = "pytorch"
 
23
  asyncio = "^3.4.3"
24
  toml = "^0.10.2"
25
  compel = "^2.0.2"
26
+ jinja2 = "^3.1.2"
27
 
28
  [[tool.poetry.source]]
29
  name = "pytorch"
templates/basic.jinja ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <style>
5
+
6
+ #title {
7
+ color: blue;
8
+ text-align: center;
9
+ margin-bottom: 50px;
10
+ }
11
+
12
+ video, audio {
13
+ display: block;
14
+ width: 90%;
15
+ margin: auto !important;
16
+ }
17
+
18
+ audio {
19
+ padding-top: 10px !important;
20
+ }
21
+
22
+ #story-body {
23
+ padding-top: 20px;
24
+ width: 50%;
25
+ margin: auto;
26
+ }
27
+
28
+ #characters {
29
+ text-align: center;
30
+ display: flex;
31
+ width: 40%;
32
+ margin: auto;
33
+ }
34
+
35
+ .character {
36
+ float: left;
37
+ padding-left: 5px;
38
+ padding-right: 5px;
39
+ }
40
+
41
+ .character p {
42
+ text-align: center;
43
+ }
44
+
45
+ .paragraphs {
46
+ text-align: justify;
47
+ padding-top: 50px;
48
+ }
49
+
50
+ </style>
51
+ </head>
52
+ <body>
53
+ <h1 id="title">
54
+ {{ title }}
55
+ </h1>
56
+
57
+ <div id="characters">
58
+ {% for character in characters %}
59
+ <div class="character">
60
+ <img src="file/{{ character['img'] }}" />
61
+ <p>{{ character['name'] }}</p>
62
+ </div>
63
+ {% endfor %}
64
+ </div>
65
+
66
+ <div></div>
67
+
68
+ <div id="story-body">
69
+ {% for item in items %}
70
+ <div>
71
+ {% if 'video' in item %}
72
+ <video controls>
73
+ <source src="file/{{ item['video'] }}" type="video/mp4">
74
+ Your browser does not support the video tag.
75
+ </video>
76
+ {% else %}
77
+ {% if 'img' in item %}
78
+ <img class="story-img" src="file/{{ item['img'] }}" />
79
+ {% endif %}
80
+ {% if 'audio' in item %}
81
+ <audio controls>
82
+ <source src="file/{{ item['audio'] }}" type="audio/wav">
83
+ Your browser does not support the audio element.
84
+ </audio>
85
+ {% endif %}
86
+ {% endif %}
87
+
88
+ <p class="paragraphs">
89
+ {{ item['story'] }}
90
+ </p>
91
+ </div>
92
+ {% endfor %}
93
+
94
+ </div>
95
+ </body>
96
+ </html>
templates/parser.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import jinja2
2
+
3
+ def gen_from_file(filename, kwargs):
4
+ # for basic template there are two keys that should be provided
5
+ # characters (list)
6
+ # - each item has 'img' and 'name' keys
7
+ #
8
+ # items (stories)
9
+ # - each item has 'video', 'img', 'audio', and 'story'
10
+ html_template = open(filename, "r").read()
11
+
12
+ environment = jinja2.Environment()
13
+ template = environment.from_string(html_template)
14
+
15
+ return template.render(**kwargs)