trydrama-neuralgarage commited on
Commit
445f52b
·
verified ·
1 Parent(s): 4ba10d6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +178 -172
app.py CHANGED
@@ -553,7 +553,7 @@ def status_html(state, title, step=0, note=""):
553
  for i, name in enumerate(STEPS, start=1)
554
  )
555
  stepper = f'<div class="dr-stepper">{dots}</div>' if state in ("working", "done") else ""
556
- icon = {"idle": "◌", "working": '<span class="dr-spin"></span>', "done": "✓", "error": "!"}[state]
557
  note_html = f'<div class="dr-status-note">{note}</div>' if note else ""
558
  return (f'<div class="dr-status dr-status-{state}">'
559
  f'<div class="dr-status-head"><span class="dr-status-icon">{icon}</span>'
@@ -629,7 +629,7 @@ def run(video, mood, excited, angry, profile: gr.OAuthProfile | None):
629
  result = watermark(raw, workdir, ctx)
630
  report(ctx, "SUCCESS")
631
  yield result, status_html("done", f"Done in {ctx.elapsed()}", 5,
632
- f"{settings_chips(drama)}<br>Use the download button on the video to save it.")
633
 
634
  except DemoError as e:
635
  if counted and e.category != "LIMIT":
@@ -660,14 +660,14 @@ except ImportError:
660
  log.warning("[startup] brand_assets.py not found; using a text logo")
661
 
662
  PRESETS = { # name: (smile −1 sad … +1 big smile, excitement, anger)
663
- "🙂 Subtle smile": (0.35, 0.0, 0.0),
664
- "😊 Big smile": (0.9, 0.3, 0.0),
665
- "😢 Sad": (-0.8, 0.0, 0.0),
666
- "🤩 Excited": (0.4, 0.9, 0.0),
667
- "😠 Angry": (0.0, 0.3, 0.9),
668
- "🎛️ Custom": None,
669
  }
670
- DEFAULT_PRESET = "😊 Big smile"
671
 
672
 
673
  def apply_preset(name):
@@ -713,6 +713,10 @@ HEAD = f"""
713
  """
714
 
715
  FAQ = [
 
 
 
 
716
  ("How do I add a smile to a video with AI?",
717
  "Upload a short clip, choose <b>Subtle smile</b> or <b>Big smile</b>, and click <b>Change "
718
  "expression</b>. DRAMA re-performs the face with a natural smile while keeping the person's "
@@ -743,31 +747,25 @@ FAQ = [
743
  ]
744
 
745
  FEATURES = [
746
- ("😊", "AI smile generator",
747
  "Add a natural smile to a neutral face, or fix a smile that looks forced. Go from a subtle, "
748
  "neutral smile to a big grin."),
749
- ("🎬", "Change the mood of a scene",
750
  "An AI video mood changer for performances: make a take sadder, angrier or more excited "
751
  "without a reshoot."),
752
- ("🎚️", "Control the intensity",
753
  "Every expression has an intensity dial, and you can blend emotions for more nuanced "
754
  "performances."),
755
- ("👥", "Edit one face in a group",
756
  "In DRAMA Studio and the API, pick which person to edit in a multi-face shot. Everyone else "
757
  "stays untouched."),
758
- ("🖼️", "Original quality kept",
759
  "Only the expression changes. Resolution, lighting and background stay exactly as they were."),
760
- ("📸", "Photos too",
761
  "DRAMA Studio is also an expression editor for photos: add a smile to a photo or change its "
762
  "mood in the same way."),
763
  ]
764
 
765
- STEPS_HOW = [
766
- ("Upload", "Add a short video with one clear face. DRAMA finds the face, scene by scene."),
767
- ("Choose", "Pick an expression and its intensity, or blend a few for a custom performance."),
768
- ("Generate & export", "DRAMA re-performs the face. Preview the result and download the MP4."),
769
- ]
770
-
771
 
772
  def logo_html(size="lg"):
773
  if LOGO_MARK_PNG and LOGO_WORDMARK_WEBP:
@@ -801,10 +799,10 @@ THEME = gr.themes.Base(
801
  border_color_primary="#25252d", border_color_primary_dark="#25252d",
802
  input_background_fill="#1b1b21", input_background_fill_dark="#1b1b21",
803
  slider_color="#f01e1e", slider_color_dark="#f01e1e",
804
- button_primary_background_fill="linear-gradient(180deg,#ff2b2b,#d40f0f)",
805
- button_primary_background_fill_dark="linear-gradient(180deg,#ff2b2b,#d40f0f)",
806
- button_primary_background_fill_hover="linear-gradient(180deg,#ff4545,#e31717)",
807
- button_primary_background_fill_hover_dark="linear-gradient(180deg,#ff4545,#e31717)",
808
  button_primary_text_color="#ffffff", button_primary_text_color_dark="#ffffff",
809
  button_secondary_background_fill="#1b1b21", button_secondary_background_fill_dark="#1b1b21",
810
  button_secondary_text_color="#ededf0", button_secondary_text_color_dark="#ededf0",
@@ -815,158 +813,173 @@ THEME = gr.themes.Base(
815
  # Every text rule is scoped under .gradio-container with !important where Gradio's own
816
  # "prose" styles would otherwise win (they load after this sheet when SSR is on).
817
  CSS = """
818
- .gradio-container { max-width: 1160px !important; margin: 0 auto !important; padding: 0 24px 40px !important; }
 
 
819
  .gradio-container .main.app { padding: 8px 0 !important; }
820
  .gradio-container .html-container { padding: 0 !important; }
821
  .gradio-container .prose { max-width: none !important; }
822
- .dr-glow { position: fixed; inset: -25vh -10vw auto; height: 75vh; pointer-events: none; z-index: 0;
823
- background: radial-gradient(55% 55% at 50% 15%, rgba(240,30,30,.18), transparent 70%); }
824
 
825
  /* header */
826
- #dr-top { align-items: center !important; padding: 18px 0 0; position: relative; z-index: 1;
827
  flex-wrap: nowrap !important; gap: 12px; }
828
  #dr-top > * { min-width: 0 !important; }
829
  .gradio-container a.dr-brand { display: inline-flex !important; align-items: center; gap: 10px;
830
  text-decoration: none !important; }
831
- .dr-mark { height: 36px !important; width: auto !important; margin: 0 !important;
832
- filter: drop-shadow(0 6px 18px rgba(240,30,30,.45)); }
833
- .dr-word { height: 22px !important; width: auto !important; margin: 0 !important; }
834
  #dr-login { max-width: 240px; margin-left: auto; flex: none !important; width: auto !important; }
835
- #dr-login button { border-radius: 999px !important; font-size: 13px !important; padding: 8px 16px !important; }
 
836
 
837
  /* hero */
838
- .gradio-container .dr-hero { text-align: center !important; padding: 40px 0 30px; position: relative; z-index: 1; }
839
- .gradio-container .dr-eyebrow { display: inline-flex; gap: 8px; align-items: center; font-size: 13px !important;
840
- color: #ffadad !important; background: rgba(240,30,30,.10); border: 1px solid rgba(240,30,30,.30);
841
- padding: 6px 14px; border-radius: 999px; }
842
- .gradio-container .dr-hero h1 { font-size: clamp(34px, 5.4vw, 60px) !important; line-height: 1.04 !important;
843
- font-weight: 800 !important; margin: 20px auto 16px !important; letter-spacing: -0.035em !important;
844
  color: #ffffff !important; text-align: center !important; max-width: 900px; }
845
- .gradio-container .dr-hero h1 .dr-grad { background: linear-gradient(90deg,#ff5a5a,#f01e1e 60%,#ff7a3d);
846
- -webkit-background-clip: text !important; background-clip: text !important;
847
- -webkit-text-fill-color: transparent !important; color: transparent !important; }
848
- .gradio-container .dr-hero .dr-sub { font-size: 18px !important; color: #b4b4be !important; max-width: 660px;
849
  margin: 0 auto !important; line-height: 1.55 !important; text-align: center !important; }
850
- .dr-facts { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 22px; }
851
- .gradio-container .dr-facts span { font-size: 13px !important; color: #c9c9d1 !important; background: #141418;
852
- border: 1px solid #25252d; padding: 6px 12px; border-radius: 999px; }
853
 
854
  /* cards */
855
- #dr-main { gap: 20px !important; position: relative; z-index: 1; align-items: stretch !important; }
856
- .dr-card { background: linear-gradient(180deg,#141418,#111115) !important; border: 1px solid #25252d !important;
857
- border-radius: 20px !important; padding: 20px !important; box-shadow: 0 24px 60px rgba(0,0,0,.4);
858
- gap: 12px !important; justify-content: flex-start !important; }
859
- .dr-card > * { flex-grow: 0 !important; }
860
- .gradio-container .dr-label { display: flex !important; align-items: center; gap: 10px; font-weight: 650 !important;
861
- font-size: 15px !important; color: #ededf0 !important; margin: 0 !important; }
862
- .gradio-container .dr-label b { width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
863
- font-size: 12px !important; background: rgba(240,30,30,.15); color: #ff7f7f !important;
864
- border: 1px solid rgba(240,30,30,.4); flex: none; }
865
- .gradio-container .dr-hint { font-size: 13px !important; color: #8b8b97 !important; margin: 4px 0 0 34px !important; }
866
- .dr-media video, .dr-media .wrap { border-radius: 14px !important; }
867
 
868
  /* presets */
869
  #dr-presets { background: transparent !important; border: 0 !important; padding: 0 !important; }
870
- #dr-presets .wrap { gap: 8px !important; }
871
- #dr-presets label { border-radius: 999px !important; border: 1px solid #2c2c35 !important; background: #1b1b21 !important;
872
- padding: 8px 14px !important; transition: all .15s ease; cursor: pointer; margin: 0 !important; }
873
- #dr-presets label:hover { border-color: #ff4d4d !important; }
874
- #dr-presets label.selected { background: rgba(240,30,30,.16) !important; border-color: #f01e1e !important;
875
- color: #fff !important; box-shadow: 0 0 0 3px rgba(240,30,30,.14); }
 
876
  #dr-presets input[type=radio] { display: none !important; }
877
 
878
  /* generate */
879
- #dr-go { height: 54px; font-size: 17px !important; font-weight: 700 !important; border-radius: 14px !important;
880
- box-shadow: 0 10px 30px rgba(240,30,30,.32); transition: transform .12s ease, box-shadow .12s ease; }
881
- #dr-go:hover { transform: translateY(-1px); box-shadow: 0 14px 36px rgba(240,30,30,.45); }
882
- .gradio-container .dr-fine { font-size: 12px !important; color: #6f6f7b !important; text-align: center !important; margin: 0 !important; }
883
 
884
  /* status */
885
- .dr-status { border-radius: 14px; padding: 14px 16px; border: 1px solid #25252d; background: #111115; }
886
  .gradio-container .dr-status-head { display: flex; align-items: center; gap: 10px; font-weight: 600 !important;
887
- font-size: 15px !important; color: #ededf0 !important; }
888
- .dr-status-icon { width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center; font-size: 13px;
889
- background: #1b1b21; color: #9a9aa6; flex: none; }
890
- .gradio-container .dr-status-note { color: #9a9aa6 !important; font-size: 13px !important; margin-top: 8px; line-height: 1.6 !important; }
891
- .dr-status-done { border-color: rgba(52,211,153,.35); background: rgba(52,211,153,.06); }
892
- .dr-status-done .dr-status-icon { background: #34d399; color: #062b1e; }
893
- .dr-status-error { border-color: rgba(248,113,113,.4); background: rgba(248,113,113,.07); }
894
- .dr-status-error .dr-status-icon { background: #f87171; color: #2b0909; font-weight: 800; }
895
- .dr-status-working { border-color: rgba(240,30,30,.4); }
896
- .dr-spin { width: 14px; height: 14px; border-radius: 50%; border: 2px solid rgba(240,30,30,.25);
897
- border-top-color: #f01e1e; animation: dr-spin .8s linear infinite; display: inline-block; }
898
  @keyframes dr-spin { to { transform: rotate(360deg); } }
899
- .dr-stepper { display: flex; gap: 6px; margin-top: 12px; }
900
- .gradio-container .dr-step { flex: 1; font-size: 12px !important; color: #6f6f7b !important; display: flex;
901
  flex-direction: column; gap: 6px; }
902
- .dr-dot { height: 4px; border-radius: 4px; background: #25252d; display: block; }
903
- .gradio-container .dr-step.is-done { color: #c9c9d1 !important; } .dr-step.is-done .dr-dot { background: #f01e1e; }
904
- .gradio-container .dr-step.is-now { color: #fff !important; font-weight: 600 !important; }
905
- .dr-step.is-now .dr-dot { background: linear-gradient(90deg,#f01e1e,#ffadad,#ff7a3d,#f01e1e);
906
- background-size: 300% 100%; animation: dr-load 1.6s linear infinite; }
907
- @keyframes dr-load { 0% { background-position: 0% 0; } 100% { background-position: 300% 0; } }
908
- .gradio-container .dr-chip { display: inline-block; font-size: 12px !important; padding: 3px 10px; border-radius: 999px;
909
- margin: 2px 4px 2px 0; background: rgba(240,30,30,.12); border: 1px solid rgba(240,30,30,.3); color: #ffadad !important; }
 
910
 
911
  /* CTA */
912
- .dr-cta { padding: 16px; border-radius: 14px; border: 1px dashed #34343e !important; display: flex; gap: 14px;
913
- align-items: center; justify-content: space-between; flex-wrap: wrap; background: rgba(255,255,255,.015); }
914
- .gradio-container .dr-cta-text { font-size: 14px !important; color: #b4b4be !important; line-height: 1.5 !important; flex: 1 1 240px; }
915
- .gradio-container .dr-cta-text b { color: #fff !important; display: block; margin-bottom: 2px; }
916
- .dr-cta-btns { display: flex; gap: 8px; }
917
- .gradio-container a.dr-btn { font-size: 14px !important; font-weight: 600 !important; color: #fff !important;
918
- text-decoration: none !important; padding: 9px 14px; border-radius: 10px; background: #1b1b21;
919
- border: 1px solid #2c2c35; white-space: nowrap; }
920
- .gradio-container a.dr-btn-primary { background: linear-gradient(180deg,#ff2b2b,#d40f0f); border: 0; }
921
-
922
- /* sections */
923
- .gradio-container .dr-section { margin: 0 !important; padding-top: 72px !important; position: relative; z-index: 1; }
924
- .gradio-container .dr-section h2 { font-size: clamp(24px, 3vw, 32px) !important; font-weight: 750 !important;
925
- letter-spacing: -0.02em !important; color: #fff !important; text-align: center !important; margin: 0 0 8px !important; }
926
- .gradio-container .dr-section .dr-section-sub { text-align: center !important; color: #9a9aa6 !important;
927
- font-size: 15px !important; margin: 0 auto 24px !important; max-width: 620px; }
928
- .dr-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
929
- .dr-tile { background: #111115; border: 1px solid #222229; border-radius: 16px; padding: 20px; }
930
- .gradio-container .dr-tile i { font-style: normal; font-size: 22px; display: block; margin-bottom: 10px; }
931
- .gradio-container .dr-tile h3 { font-size: 16px !important; font-weight: 650 !important; color: #fff !important;
 
 
 
 
 
 
932
  margin: 0 0 6px !important; }
933
- .gradio-container .dr-tile span { font-size: 14px !important; color: #9a9aa6 !important; line-height: 1.55 !important; }
934
- .gradio-container .dr-num { display: inline-grid !important; place-items: center; width: 28px; height: 28px; border-radius: 8px;
935
- font-weight: 700 !important; font-size: 13px !important; color: #fff !important; margin-bottom: 12px;
936
- background: linear-gradient(180deg,#ff2b2b,#d40f0f); }
937
 
938
  /* FAQ */
939
- .dr-faq { max-width: 820px; margin: 0 auto; display: grid; gap: 10px; }
940
- .gradio-container .dr-faq details { background: #111115; border: 1px solid #222229; border-radius: 14px; padding: 0 !important; }
941
- .gradio-container .dr-faq summary { cursor: pointer; list-style: none; padding: 16px 48px 16px 18px !important;
942
- font-size: 15px !important; font-weight: 600 !important; color: #ededf0 !important; position: relative; }
943
- .dr-faq summary::-webkit-details-marker { display: none; }
944
- .dr-faq summary::after { content: "+"; position: absolute; right: 18px; top: 50%; transform: translateY(-50%);
945
- font-size: 20px; color: #ff7f7f; transition: transform .2s; }
946
- .dr-faq details[open] summary::after { transform: translateY(-50%) rotate(45deg); }
947
- .gradio-container .dr-faq .dr-ans { padding: 0 18px 16px !important; color: #a8a8b3 !important; font-size: 14px !important;
948
- line-height: 1.6 !important; margin: 0 !important; }
949
- .gradio-container .dr-faq a, .gradio-container .dr-foot a { color: #ff9a9a !important; text-decoration: none !important; }
 
 
 
 
 
 
950
 
951
  /* footer */
952
- .gradio-container .dr-foot { text-align: center !important; color: #6f6f7b !important; font-size: 13px !important;
953
- padding: 48px 0 0; line-height: 1.8 !important; position: relative; z-index: 1; }
954
- .dr-foot .dr-brand { margin-bottom: 10px; }
955
- .dr-foot .dr-mark { height: 28px !important; } .dr-foot .dr-word { height: 17px !important; }
956
- .gradio-container .dr-foot nav a { color: #b4b4be !important; margin: 0 9px; }
 
957
  .gradio-container .dr-foot nav a:hover { color: #fff !important; }
 
958
 
959
- @media (max-width: 860px) { .dr-grid { grid-template-columns: repeat(2, 1fr); } }
 
 
 
960
  @media (max-width: 640px) {
961
  .gradio-container { padding: 0 16px 32px !important; }
962
- .dr-grid { grid-template-columns: 1fr; }
963
- .gradio-container .dr-hero { padding: 26px 0 20px; }
964
  .gradio-container .dr-hero .dr-sub { font-size: 16px !important; }
965
- .dr-card { padding: 16px !important; border-radius: 16px !important; }
966
- .dr-mark { height: 30px !important; } .dr-word { height: 18px !important; }
967
- #dr-login button { font-size: 12px !important; padding: 6px 12px !important; }
968
- .dr-cta-btns { width: 100%; } .gradio-container a.dr-btn { flex: 1; text-align: center; }
969
- .gradio-container .dr-section { padding-top: 52px !important; }
 
 
 
 
970
  }
971
  """
972
 
@@ -982,24 +995,24 @@ with gr.Blocks(title=PAGE_TITLE) as demo:
982
 
983
  gr.HTML(f"""
984
  <header class="dr-hero">
985
- <div class="dr-eyebrow">🎭 AI facial expression editor · free online demo</div>
986
- <h1>Change facial expressions<br><span class="dr-grad">with one click.</span></h1>
987
  <div class="dr-sub">DRAMA is an AI facial expression changer for video. Add a natural smile,
988
  or make a scene sadder, angrier or more excited, and keep the original take. No reshoot needed.</div>
989
- <div class="dr-facts"><span>⏱ Clips up to {int(MAX_SECONDS)} s</span>
990
- <span>🎁 {PER_USER_DAILY} free runs a day</span><span>✨ Original quality kept</span></div>
991
  </header>""")
992
 
993
  with gr.Row(elem_id="dr-main", equal_height=True):
994
  with gr.Column(scale=1, elem_classes="dr-card"):
995
- gr.HTML('<div class="dr-label"><b>1</b>Upload a clip</div>'
996
  '<div class="dr-hint">One person, face clearly visible, front-on. .mp4 or .mov.</div>')
997
  video_in = gr.Video(show_label=False, sources=["upload"], height=320, elem_classes="dr-media")
998
  samples = [[p] for p in sorted(glob.glob("examples/*.mp4"))]
999
  if samples:
1000
  gr.Examples(samples, inputs=[video_in], label="No clip handy? Try one of these")
1001
 
1002
- gr.HTML('<div class="dr-label"><b>2</b>Pick an expression</div>')
1003
  preset = gr.Radio(list(PRESETS), value=DEFAULT_PRESET, show_label=False,
1004
  container=False, elem_id="dr-presets")
1005
  d_mood, d_exc, d_ang = PRESETS[DEFAULT_PRESET]
@@ -1008,13 +1021,13 @@ with gr.Blocks(title=PAGE_TITLE) as demo:
1008
  info="−1 very sad · 0 neutral · +1 big smile")
1009
  excited = gr.Slider(0, 1, value=d_exc, step=0.05, label="Excitement")
1010
  angry = gr.Slider(0, 1, value=d_ang, step=0.05, label="Anger")
1011
- go = gr.Button("✨ Change expression", variant="primary", elem_id="dr-go")
1012
  gr.HTML('<div class="dr-fine">Only upload footage you have the right to use. '
1013
  'Demo results are watermarked.</div>')
1014
 
1015
  with gr.Column(scale=1, elem_classes="dr-card"):
1016
- gr.HTML('<div class="dr-label"><b>3</b>Your result</div>'
1017
- '<div class="dr-hint">Download the finished MP4 from the video\'s menu.</div>')
1018
  video_out = gr.Video(show_label=False, interactive=False, height=320, elem_classes="dr-media")
1019
  status_out = gr.HTML(IDLE)
1020
  gr.HTML(f"""
@@ -1022,47 +1035,40 @@ with gr.Blocks(title=PAGE_TITLE) as demo:
1022
  DRAMA Studio does full-length video up to 4K, photos, and multi-face control.
1023
  The free plan includes 60 credits.</div>
1024
  <div class="dr-cta-btns"><a class="dr-btn" href="{DOCS}" target="_blank">API docs</a>
1025
- <a class="dr-btn dr-btn-primary" href="{SITE}" target="_blank">Try DRAMA free →</a></div></div>""")
1026
-
1027
- features = "".join(f'<div class="dr-tile"><i>{icon}</i><h3>{title}</h3><span>{text}</span></div>'
1028
- for icon, title, text in FEATURES)
1029
- gr.HTML(f"""
1030
- <section class="dr-section">
1031
- <h2>An AI face expression changer for real footage</h2>
1032
- <div class="dr-section-sub">From a subtle smile to a full mood change, DRAMA edits the performance
1033
- and leaves everything else in the shot alone.</div>
1034
- <div class="dr-grid">{features}</div>
1035
- </section>""")
1036
 
1037
- steps = "".join(f'<div class="dr-tile"><span class="dr-num">{i}</span><h3>{t}</h3><span>{d}</span></div>'
1038
- for i, (t, d) in enumerate(STEPS_HOW, start=1))
1039
  gr.HTML(f"""
1040
- <section class="dr-section">
1041
- <h2>How to change a facial expression in a video</h2>
1042
- <div class="dr-section-sub">Three steps, about a minute.</div>
1043
- <div class="dr-grid">{steps}</div>
 
 
1044
  </section>""")
1045
 
1046
  faq = "".join(f'<details><summary>{q}</summary><div class="dr-ans">{a}</div></details>' for q, a in FAQ)
1047
  gr.HTML(f"""
1048
- <section class="dr-section">
1049
- <h2>Frequently asked questions</h2>
1050
- <div class="dr-section-sub">About DRAMA, the AI expression editor.</div>
 
 
1051
  <div class="dr-faq">{faq}</div>
1052
  </section>""")
1053
 
1054
  gr.HTML(f"""
1055
  <footer class="dr-foot">{logo_html("sm")}
1056
  <nav><a href="{CARD}" target="_blank">Model card</a><a href="{DOCS}" target="_blank">API docs</a><a href="{PRICING}" target="_blank">Pricing</a><a href="https://trydrama.io/privacypolicy/" target="_blank">Privacy</a><a href="https://trydrama.io/termsofuses/" target="_blank">Terms</a></nav>
1057
- <div>DRAMA by SyncMonster is an AI facial expression editor for changing emotion, mood and
1058
- performance without reshoots.</div>
1059
- <div>© 2026 SyncMonster · NeuralGarage Pvt. Ltd., Bengaluru</div>
1060
  </footer>""")
1061
 
1062
  preset.change(apply_preset, inputs=[preset], outputs=[mood, excited, angry],
1063
  api_visibility="private", show_progress="hidden")
1064
  for slider in (mood, excited, angry):
1065
- slider.release(lambda: "🎛️ Custom", outputs=[preset],
1066
  api_visibility="private", show_progress="hidden")
1067
  video_in.upload(prepare_upload, inputs=[video_in], outputs=[video_in], api_visibility="private")
1068
  video_in.clear(lambda: (None, IDLE), outputs=[video_out, status_out], api_visibility="private")
@@ -1088,4 +1094,4 @@ def favicon_file():
1088
 
1089
  startup_check()
1090
  demo.queue(max_size=20).launch(theme=THEME, css=CSS, js=FORCE_DARK, head=HEAD,
1091
- favicon_path=favicon_file(), footer_links=[])
 
553
  for i, name in enumerate(STEPS, start=1)
554
  )
555
  stepper = f'<div class="dr-stepper">{dots}</div>' if state in ("working", "done") else ""
556
+ icon = {"idle": "", "working": '<span class="dr-spin"></span>', "done": "✓", "error": "!"}[state]
557
  note_html = f'<div class="dr-status-note">{note}</div>' if note else ""
558
  return (f'<div class="dr-status dr-status-{state}">'
559
  f'<div class="dr-status-head"><span class="dr-status-icon">{icon}</span>'
 
629
  result = watermark(raw, workdir, ctx)
630
  report(ctx, "SUCCESS")
631
  yield result, status_html("done", f"Done in {ctx.elapsed()}", 5,
632
+ settings_chips(drama))
633
 
634
  except DemoError as e:
635
  if counted and e.category != "LIMIT":
 
660
  log.warning("[startup] brand_assets.py not found; using a text logo")
661
 
662
  PRESETS = { # name: (smile −1 sad … +1 big smile, excitement, anger)
663
+ "Subtle smile": (0.35, 0.0, 0.0),
664
+ "Big smile": (0.9, 0.3, 0.0),
665
+ "Sad": (-0.8, 0.0, 0.0),
666
+ "Excited": (0.4, 0.9, 0.0),
667
+ "Angry": (0.0, 0.3, 0.9),
668
+ "Custom": None,
669
  }
670
+ DEFAULT_PRESET = "Big smile"
671
 
672
 
673
  def apply_preset(name):
 
713
  """
714
 
715
  FAQ = [
716
+ ("How do I change a facial expression in a video?",
717
+ "Upload a short clip with one clear face, pick an expression and how strong it should be, "
718
+ "then click <b>Change expression</b>. DRAMA finds the face scene by scene, re-performs it, "
719
+ "and gives you an MP4 to download. It takes about a minute."),
720
  ("How do I add a smile to a video with AI?",
721
  "Upload a short clip, choose <b>Subtle smile</b> or <b>Big smile</b>, and click <b>Change "
722
  "expression</b>. DRAMA re-performs the face with a natural smile while keeping the person's "
 
747
  ]
748
 
749
  FEATURES = [
750
+ ("AI smile generator",
751
  "Add a natural smile to a neutral face, or fix a smile that looks forced. Go from a subtle, "
752
  "neutral smile to a big grin."),
753
+ ("Change the mood of a scene",
754
  "An AI video mood changer for performances: make a take sadder, angrier or more excited "
755
  "without a reshoot."),
756
+ ("Control the intensity",
757
  "Every expression has an intensity dial, and you can blend emotions for more nuanced "
758
  "performances."),
759
+ ("Edit one face in a group",
760
  "In DRAMA Studio and the API, pick which person to edit in a multi-face shot. Everyone else "
761
  "stays untouched."),
762
+ ("Original quality kept",
763
  "Only the expression changes. Resolution, lighting and background stay exactly as they were."),
764
+ ("Photos too",
765
  "DRAMA Studio is also an expression editor for photos: add a smile to a photo or change its "
766
  "mood in the same way."),
767
  ]
768
 
 
 
 
 
 
 
769
 
770
  def logo_html(size="lg"):
771
  if LOGO_MARK_PNG and LOGO_WORDMARK_WEBP:
 
799
  border_color_primary="#25252d", border_color_primary_dark="#25252d",
800
  input_background_fill="#1b1b21", input_background_fill_dark="#1b1b21",
801
  slider_color="#f01e1e", slider_color_dark="#f01e1e",
802
+ button_primary_background_fill="#e3141c",
803
+ button_primary_background_fill_dark="#e3141c",
804
+ button_primary_background_fill_hover="#f02a31",
805
+ button_primary_background_fill_hover_dark="#f02a31",
806
  button_primary_text_color="#ffffff", button_primary_text_color_dark="#ffffff",
807
  button_secondary_background_fill="#1b1b21", button_secondary_background_fill_dark="#1b1b21",
808
  button_secondary_text_color="#ededf0", button_secondary_text_color_dark="#ededf0",
 
813
  # Every text rule is scoped under .gradio-container with !important where Gradio's own
814
  # "prose" styles would otherwise win (they load after this sheet when SSR is on).
815
  CSS = """
816
+ :root { --dr-red: #e3141c; --dr-red-soft: rgba(227,20,28,.12); --dr-line: #2a2a32; --dr-card: #121216;
817
+ --dr-text: #ededf0; --dr-muted: #9a9aa6; --dr-faint: #6b6b76; }
818
+ .gradio-container { max-width: 1120px !important; margin: 0 auto !important; padding: 0 24px 48px !important; }
819
  .gradio-container .main.app { padding: 8px 0 !important; }
820
  .gradio-container .html-container { padding: 0 !important; }
821
  .gradio-container .prose { max-width: none !important; }
822
+ .gradio-container .dr-glow { position: fixed; inset: -30vh 0 auto; height: 70vh; pointer-events: none; z-index: 0;
823
+ background: radial-gradient(45% 50% at 50% 0%, rgba(227,20,28,.10), transparent 70%); }
824
 
825
  /* header */
826
+ #dr-top { align-items: center !important; padding: 20px 0 0; position: relative; z-index: 1;
827
  flex-wrap: nowrap !important; gap: 12px; }
828
  #dr-top > * { min-width: 0 !important; }
829
  .gradio-container a.dr-brand { display: inline-flex !important; align-items: center; gap: 10px;
830
  text-decoration: none !important; }
831
+ .gradio-container .dr-mark { height: 32px !important; width: auto !important; margin: 0 !important; }
832
+ .gradio-container .dr-word { height: 20px !important; width: auto !important; margin: 0 !important; }
 
833
  #dr-login { max-width: 240px; margin-left: auto; flex: none !important; width: auto !important; }
834
+ #dr-login button { border-radius: 8px !important; font-size: 13px !important; padding: 7px 14px !important;
835
+ background: transparent !important; border: 1px solid #2c2c35 !important; }
836
 
837
  /* hero */
838
+ .gradio-container .dr-hero { text-align: center !important; padding: 64px 0 44px; position: relative; z-index: 1; }
839
+ .gradio-container .dr-kicker { font-size: 12px !important; font-weight: 600 !important; letter-spacing: .12em !important;
840
+ text-transform: uppercase; color: #ff6b6b !important; margin: 0 0 14px !important; }
841
+ .gradio-container .dr-hero h1 { font-size: clamp(36px, 5.6vw, 64px) !important; line-height: 1.02 !important;
842
+ font-weight: 750 !important; margin: 0 auto 18px !important; letter-spacing: -0.04em !important;
 
843
  color: #ffffff !important; text-align: center !important; max-width: 900px; }
844
+ .gradio-container .dr-hero h1 .dr-accent { color: var(--dr-red) !important; }
845
+ .gradio-container .dr-hero .dr-sub { font-size: 18px !important; color: #a8a8b3 !important; max-width: 600px;
 
 
846
  margin: 0 auto !important; line-height: 1.55 !important; text-align: center !important; }
847
+ .gradio-container .dr-meta { display: flex; justify-content: center; flex-wrap: wrap; margin-top: 22px; }
848
+ .gradio-container .dr-meta span { font-size: 13px !important; color: var(--dr-faint) !important; }
849
+ .gradio-container .dr-meta span + span::before { content: "·"; margin: 0 10px; color: #3a3a44; }
850
 
851
  /* cards */
852
+ #dr-main { gap: 16px !important; position: relative; z-index: 1; align-items: stretch !important; }
853
+ .gradio-container .dr-card { background: var(--dr-card) !important; border: 1px solid var(--dr-line) !important;
854
+ border-radius: 16px !important; padding: 20px !important; gap: 12px !important;
855
+ justify-content: flex-start !important; }
856
+ .gradio-container .dr-card > * { flex-grow: 0 !important; }
857
+ .gradio-container .dr-label { display: flex !important; align-items: baseline; gap: 10px; font-weight: 600 !important;
858
+ font-size: 15px !important; color: var(--dr-text) !important; margin: 0 !important; }
859
+ .gradio-container .dr-label b { font: 600 12px/1 ui-monospace, "SF Mono", Menlo, monospace !important;
860
+ color: var(--dr-faint) !important; }
861
+ .gradio-container .dr-hint { font-size: 13px !important; color: var(--dr-faint) !important; margin: 4px 0 0 !important; }
862
+ .gradio-container .dr-media video, .gradio-container .dr-media .wrap { border-radius: 10px !important; }
 
863
 
864
  /* presets */
865
  #dr-presets { background: transparent !important; border: 0 !important; padding: 0 !important; }
866
+ #dr-presets .wrap { gap: 6px !important; }
867
+ #dr-presets label { border-radius: 8px !important; border: 1px solid #2a2a33 !important; background: transparent !important;
868
+ padding: 7px 13px !important; transition: border-color .15s ease, color .15s ease; cursor: pointer; margin: 0 !important;
869
+ color: #b4b4be !important; font-size: 14px !important; }
870
+ #dr-presets label:hover { border-color: #4a4a55 !important; color: #fff !important; }
871
+ #dr-presets label.selected { background: var(--dr-red-soft) !important; border-color: var(--dr-red) !important;
872
+ color: #fff !important; }
873
  #dr-presets input[type=radio] { display: none !important; }
874
 
875
  /* generate */
876
+ #dr-go { height: 50px; font-size: 16px !important; font-weight: 600 !important; border-radius: 10px !important;
877
+ box-shadow: none !important; transition: background .15s ease; }
878
+ .gradio-container .dr-fine { font-size: 12px !important; color: var(--dr-faint) !important; text-align: center !important; margin: 0 !important; }
 
879
 
880
  /* status */
881
+ .gradio-container .dr-status { border-radius: 10px; padding: 14px 16px; border: 1px solid var(--dr-line); background: #0e0e11; }
882
  .gradio-container .dr-status-head { display: flex; align-items: center; gap: 10px; font-weight: 600 !important;
883
+ font-size: 14px !important; color: var(--dr-text) !important; }
884
+ .gradio-container .dr-status-icon { width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center; font-size: 12px; flex: none; }
885
+ .gradio-container .dr-status-idle .dr-status-icon { width: 8px; height: 8px; background: #3a3a44; }
886
+ .gradio-container .dr-status-note { color: var(--dr-muted) !important; font-size: 13px !important; margin-top: 8px; line-height: 1.6 !important; }
887
+ .gradio-container .dr-status-done .dr-status-icon { background: #22c55e; color: #04210f; font-weight: 700; }
888
+ .gradio-container .dr-status-error { border-color: rgba(239,68,68,.35); }
889
+ .gradio-container .dr-status-error .dr-status-icon { background: #ef4444; color: #fff; font-weight: 700; }
890
+ .gradio-container .dr-spin { width: 14px; height: 14px; border-radius: 50%; border: 2px solid #2c2c35;
891
+ border-top-color: var(--dr-red); animation: dr-spin .8s linear infinite; display: inline-block; }
 
 
892
  @keyframes dr-spin { to { transform: rotate(360deg); } }
893
+ .gradio-container .dr-stepper { display: flex; gap: 6px; margin-top: 12px; }
894
+ .gradio-container .dr-step { flex: 1; font-size: 12px !important; color: var(--dr-faint) !important; display: flex;
895
  flex-direction: column; gap: 6px; }
896
+ .gradio-container .dr-dot { height: 3px; border-radius: 3px; background: #26262e; display: block; }
897
+ .gradio-container .dr-step.is-done { color: #b4b4be !important; } .dr-step.is-done .dr-dot { background: var(--dr-red); }
898
+ .gradio-container .dr-step.is-now { color: #fff !important; }
899
+ .gradio-container .dr-step.is-now .dr-dot { background: linear-gradient(90deg, #26262e 0%, var(--dr-red) 50%, #26262e 100%);
900
+ background-size: 200% 100%; animation: dr-load 1.4s linear infinite; }
901
+ @keyframes dr-load { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
902
+ .gradio-container .dr-chip { display: inline-block; font: 500 12px/1.4 ui-monospace, "SF Mono", Menlo, monospace !important;
903
+ padding: 3px 8px; border-radius: 6px; margin: 2px 4px 2px 0; background: #1b1b21;
904
+ border: 1px solid #2a2a33; color: #c9c9d1 !important; }
905
 
906
  /* CTA */
907
+ .gradio-container .dr-cta { padding: 16px 18px; border-radius: 10px; border: 1px solid var(--dr-line); display: flex; gap: 14px;
908
+ align-items: center; justify-content: space-between; flex-wrap: wrap; }
909
+ .gradio-container .dr-cta-text { font-size: 13px !important; color: var(--dr-muted) !important; line-height: 1.55 !important; flex: 1 1 240px; }
910
+ .gradio-container .dr-cta-text b { color: #fff !important; display: block; margin-bottom: 2px; font-size: 14px; }
911
+ .gradio-container .dr-cta-btns { display: flex; gap: 8px; }
912
+ .gradio-container a.dr-btn { font-size: 13px !important; font-weight: 600 !important; color: #fff !important;
913
+ text-decoration: none !important; padding: 8px 14px; border-radius: 8px; border: 1px solid #2c2c35; white-space: nowrap; }
914
+ .gradio-container a.dr-btn:hover { border-color: #4a4a55; }
915
+ .gradio-container a.dr-btn-primary { background: var(--dr-red); border-color: var(--dr-red); }
916
+ .gradio-container a.dr-btn-primary:hover { background: #f02a31; border-color: #f02a31; }
917
+
918
+ /* editorial sections */
919
+ .gradio-container .dr-section { margin: 0 !important; padding-top: 96px !important; position: relative; z-index: 1; }
920
+ .gradio-container .dr-split { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 56px; align-items: start; }
921
+ .gradio-container .dr-split-head { position: sticky; top: 24px; }
922
+ .gradio-container .dr-section h2 { font-size: clamp(26px, 3vw, 34px) !important; font-weight: 700 !important;
923
+ letter-spacing: -0.025em !important; line-height: 1.15 !important; color: #fff !important; margin: 0 0 12px !important;
924
+ text-align: left !important; }
925
+ .gradio-container .dr-section .dr-section-sub { color: var(--dr-muted) !important; font-size: 15px !important;
926
+ line-height: 1.6 !important; margin: 0 !important; text-align: left !important; }
927
+ .gradio-container .dr-section-sub a { color: #fff !important; text-decoration: underline !important;
928
+ padding: 0 !important; margin: 0 !important; background: none !important;
929
+ text-underline-offset: 3px; text-decoration-color: #4a4a55 !important; }
930
+ .gradio-container .dr-list { display: grid; grid-template-columns: 1fr 1fr; column-gap: 32px; }
931
+ .gradio-container .dr-item { border-top: 1px solid var(--dr-line); padding: 18px 0 24px; }
932
+ .gradio-container .dr-item h3 { font-size: 15px !important; font-weight: 600 !important; color: #fff !important;
933
  margin: 0 0 6px !important; }
934
+ .gradio-container .dr-item span { font-size: 14px !important; color: var(--dr-muted) !important; line-height: 1.55 !important; }
 
 
 
935
 
936
  /* FAQ */
937
+ .gradio-container .dr-faq { display: grid; border-top: 1px solid var(--dr-line); }
938
+ .gradio-container .dr-faq details { border-bottom: 1px solid var(--dr-line); padding: 0 !important; margin: 0 !important;
939
+ background: transparent !important; }
940
+ .gradio-container .dr-faq summary { cursor: pointer; list-style: none; padding: 18px 40px 18px 0 !important;
941
+ font-size: 15px !important; font-weight: 500 !important; color: var(--dr-text) !important; position: relative; }
942
+ .gradio-container .dr-faq summary::-webkit-details-marker { display: none; }
943
+ .gradio-container .dr-faq summary::after { content: ""; position: absolute; right: 6px; top: 50%; width: 8px; height: 8px;
944
+ border-right: 1.5px solid #8b8b97; border-bottom: 1.5px solid #8b8b97; transform: translateY(-75%) rotate(45deg);
945
+ transition: transform .15s; }
946
+ .gradio-container .dr-faq details[open] summary::after { transform: translateY(-25%) rotate(-135deg); }
947
+ .gradio-container .dr-faq summary:hover { color: #fff !important; }
948
+ .gradio-container .dr-faq .dr-ans { padding: 0 40px 20px 0 !important; color: var(--dr-muted) !important;
949
+ font-size: 14px !important; line-height: 1.65 !important; margin: 0 !important; }
950
+ .gradio-container .dr-faq a { color: #fff !important; text-decoration: underline !important;
951
+ padding: 0 !important; margin: 0 !important; background: none !important;
952
+ text-underline-offset: 3px; text-decoration-color: #4a4a55 !important; }
953
+ .gradio-container .dr-faq b { color: #d4d4da !important; font-weight: 600 !important; }
954
 
955
  /* footer */
956
+ .gradio-container .dr-foot { color: var(--dr-faint) !important; font-size: 13px !important; margin-top: 96px;
957
+ padding: 28px 0 0; border-top: 1px solid var(--dr-line); line-height: 1.8 !important; position: relative; z-index: 1;
958
+ display: grid; grid-template-columns: auto 1fr; gap: 6px 32px; align-items: center; }
959
+ .gradio-container .dr-foot .dr-mark { height: 24px !important; } .dr-foot .dr-word { height: 15px !important; }
960
+ .gradio-container .dr-foot nav { justify-self: end; }
961
+ .gradio-container .dr-foot nav a { color: var(--dr-muted) !important; margin-left: 20px; text-decoration: none !important; }
962
  .gradio-container .dr-foot nav a:hover { color: #fff !important; }
963
+ .gradio-container .dr-foot .dr-foot-copy { grid-column: 1 / -1; }
964
 
965
+ @media (max-width: 900px) {
966
+ .gradio-container .dr-split { grid-template-columns: 1fr; gap: 24px; }
967
+ .gradio-container .dr-split-head { position: static; }
968
+ }
969
  @media (max-width: 640px) {
970
  .gradio-container { padding: 0 16px 32px !important; }
971
+ .gradio-container .dr-list { grid-template-columns: 1fr; }
972
+ .gradio-container .dr-hero { padding: 40px 0 28px; }
973
  .gradio-container .dr-hero .dr-sub { font-size: 16px !important; }
974
+ .gradio-container .dr-meta { gap: 4px 14px; }
975
+ .gradio-container .dr-meta span + span::before { content: none; }
976
+ .gradio-container .dr-card { padding: 16px !important; }
977
+ .gradio-container .dr-mark { height: 28px !important; } .dr-word { height: 17px !important; }
978
+ #dr-login button { font-size: 12px !important; padding: 6px 10px !important; }
979
+ .gradio-container .dr-cta-btns { width: 100%; } .gradio-container a.dr-btn { flex: 1; text-align: center; }
980
+ .gradio-container .dr-section { padding-top: 64px !important; }
981
+ .gradio-container .dr-foot { grid-template-columns: 1fr; margin-top: 64px; }
982
+ .gradio-container .dr-foot nav { justify-self: start; } .gradio-container .dr-foot nav a { margin: 0 16px 0 0; }
983
  }
984
  """
985
 
 
995
 
996
  gr.HTML(f"""
997
  <header class="dr-hero">
998
+ <div class="dr-kicker">AI facial expression editor</div>
999
+ <h1>Change facial expressions<br><span class="dr-accent">with one click.</span></h1>
1000
  <div class="dr-sub">DRAMA is an AI facial expression changer for video. Add a natural smile,
1001
  or make a scene sadder, angrier or more excited, and keep the original take. No reshoot needed.</div>
1002
+ <div class="dr-meta"><span>Free to try</span><span>Clips up to {int(MAX_SECONDS)} seconds</span>
1003
+ <span>{PER_USER_DAILY} runs a day</span><span>Original quality kept</span></div>
1004
  </header>""")
1005
 
1006
  with gr.Row(elem_id="dr-main", equal_height=True):
1007
  with gr.Column(scale=1, elem_classes="dr-card"):
1008
+ gr.HTML('<div class="dr-label"><b>01</b>Upload a clip</div>'
1009
  '<div class="dr-hint">One person, face clearly visible, front-on. .mp4 or .mov.</div>')
1010
  video_in = gr.Video(show_label=False, sources=["upload"], height=320, elem_classes="dr-media")
1011
  samples = [[p] for p in sorted(glob.glob("examples/*.mp4"))]
1012
  if samples:
1013
  gr.Examples(samples, inputs=[video_in], label="No clip handy? Try one of these")
1014
 
1015
+ gr.HTML('<div class="dr-label"><b>02</b>Pick an expression</div>')
1016
  preset = gr.Radio(list(PRESETS), value=DEFAULT_PRESET, show_label=False,
1017
  container=False, elem_id="dr-presets")
1018
  d_mood, d_exc, d_ang = PRESETS[DEFAULT_PRESET]
 
1021
  info="−1 very sad · 0 neutral · +1 big smile")
1022
  excited = gr.Slider(0, 1, value=d_exc, step=0.05, label="Excitement")
1023
  angry = gr.Slider(0, 1, value=d_ang, step=0.05, label="Anger")
1024
+ go = gr.Button("Change expression", variant="primary", elem_id="dr-go")
1025
  gr.HTML('<div class="dr-fine">Only upload footage you have the right to use. '
1026
  'Demo results are watermarked.</div>')
1027
 
1028
  with gr.Column(scale=1, elem_classes="dr-card"):
1029
+ gr.HTML('<div class="dr-label"><b>03</b>Your result</div>'
1030
+ '<div class="dr-hint">Your edited MP4 appears here, ready to download.</div>')
1031
  video_out = gr.Video(show_label=False, interactive=False, height=320, elem_classes="dr-media")
1032
  status_out = gr.HTML(IDLE)
1033
  gr.HTML(f"""
 
1035
  DRAMA Studio does full-length video up to 4K, photos, and multi-face control.
1036
  The free plan includes 60 credits.</div>
1037
  <div class="dr-cta-btns"><a class="dr-btn" href="{DOCS}" target="_blank">API docs</a>
1038
+ <a class="dr-btn dr-btn-primary" href="{SITE}" target="_blank">Try DRAMA Studio</a></div></div>""")
 
 
 
 
 
 
 
 
 
 
1039
 
1040
+ features = "".join(f'<div class="dr-item"><h3>{title}</h3><span>{text}</span></div>'
1041
+ for title, text in FEATURES)
1042
  gr.HTML(f"""
1043
+ <section class="dr-section dr-split">
1044
+ <div class="dr-split-head"><div class="dr-kicker">What it does</div>
1045
+ <h2>An AI face expression changer for real footage</h2>
1046
+ <div class="dr-section-sub">From a subtle smile to a full mood change, DRAMA edits the
1047
+ performance and leaves everything else in the shot alone.</div></div>
1048
+ <div class="dr-list">{features}</div>
1049
  </section>""")
1050
 
1051
  faq = "".join(f'<details><summary>{q}</summary><div class="dr-ans">{a}</div></details>' for q, a in FAQ)
1052
  gr.HTML(f"""
1053
+ <section class="dr-section dr-split">
1054
+ <div class="dr-split-head"><div class="dr-kicker">FAQ</div>
1055
+ <h2>Frequently asked questions</h2>
1056
+ <div class="dr-section-sub">About DRAMA, the AI expression editor. Something else?
1057
+ <a href="https://trydrama.io/gethelp/" target="_blank">Contact us</a>.</div></div>
1058
  <div class="dr-faq">{faq}</div>
1059
  </section>""")
1060
 
1061
  gr.HTML(f"""
1062
  <footer class="dr-foot">{logo_html("sm")}
1063
  <nav><a href="{CARD}" target="_blank">Model card</a><a href="{DOCS}" target="_blank">API docs</a><a href="{PRICING}" target="_blank">Pricing</a><a href="https://trydrama.io/privacypolicy/" target="_blank">Privacy</a><a href="https://trydrama.io/termsofuses/" target="_blank">Terms</a></nav>
1064
+ <div class="dr-foot-copy">DRAMA by SyncMonster is an AI facial expression editor for changing emotion,
1065
+ mood and performance without reshoots.<br>© 2026 SyncMonster · NeuralGarage Pvt. Ltd., Bengaluru</div>
 
1066
  </footer>""")
1067
 
1068
  preset.change(apply_preset, inputs=[preset], outputs=[mood, excited, angry],
1069
  api_visibility="private", show_progress="hidden")
1070
  for slider in (mood, excited, angry):
1071
+ slider.release(lambda: "Custom", outputs=[preset],
1072
  api_visibility="private", show_progress="hidden")
1073
  video_in.upload(prepare_upload, inputs=[video_in], outputs=[video_in], api_visibility="private")
1074
  video_in.clear(lambda: (None, IDLE), outputs=[video_out, status_out], api_visibility="private")
 
1094
 
1095
  startup_check()
1096
  demo.queue(max_size=20).launch(theme=THEME, css=CSS, js=FORCE_DARK, head=HEAD,
1097
+ favicon_path=favicon_file(), footer_links=[])