rahul7star commited on
Commit
7c52de2
·
verified ·
1 Parent(s): 5398122

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +165 -78
app.py CHANGED
@@ -156,31 +156,36 @@ def download_markdown(path: str) -> str:
156
 
157
  def make_story_card(story: dict) -> str:
158
  title = html.escape(story["title"])
159
-
160
- # Convert timestamp → readable time
161
- created_time = format_ts(story["timestamp"])
162
 
163
  story_md = download_markdown(story["markdown_path"])
164
  story_md_html = gr.Markdown().postprocess(story_md)
165
 
166
- # 🎬 Video
167
  video_html = ""
168
  if story["video_path"]:
169
  video_url = build_resolve_url(story["video_path"])
170
  video_html = f"""
171
- <video class="story-video" controls preload="metadata">
172
- <source src="{video_url}" type="video/mp4">
173
- </video>
 
 
 
174
  """
175
 
176
- # 🖼 Images
177
  gallery_html = ""
178
  if story["image_paths"]:
179
  imgs = []
180
  for img_path in story["image_paths"]:
181
  img_url = build_resolve_url(img_path)
 
182
  imgs.append(
183
- f'<img src="{img_url}" class="story-image" loading="lazy">'
 
 
 
 
184
  )
185
  gallery_html = f"""
186
  <div class="story-gallery">
@@ -188,29 +193,44 @@ def make_story_card(story: dict) -> str:
188
  </div>
189
  """
190
 
 
 
 
 
 
 
 
191
  return f"""
192
  <section class="story-card">
193
-
194
- <!-- ✅ TITLE -->
195
- <h2 class="story-title">{title}</h2>
196
-
197
- <!-- ✅ NEW: GENERATED TIME -->
198
- <div class="story-time">Generated on: {created_time}</div>
 
 
 
 
 
199
 
200
  {video_html}
201
 
202
  <div class="story-content">
203
  <div class="story-text">
204
- {story_md_html}
 
205
  </div>
206
 
207
  <div class="story-visuals">
208
- {gallery_html}
 
209
  </div>
210
  </div>
211
-
212
  </section>
213
  """
 
 
214
  def build_showcase_html():
215
  stories = collect_stories()
216
 
@@ -229,7 +249,7 @@ def build_showcase_html():
229
  <div class="hero">
230
  <div>
231
  <div class="eyebrow">Story Showcase</div>
232
- <h1>OhamLab Story Factory</h1>
233
  <p>Latest generated stories, images, and videos from the dataset repo.</p>
234
  </div>
235
  <div class="hero-stat">
@@ -254,176 +274,244 @@ def refresh_showcase():
254
  # =========================================================
255
  CUSTOM_CSS = """
256
  :root {
257
- --bg: #f5f7fb;
258
- --panel: #ffffff;
259
- --panel-2: #f9fafc;
260
- --border: rgba(0,0,0,0.08);
261
- --text: #1a1f36;
262
- --muted: #6b7280;
263
- --accent: #2563eb;
264
- --accent-2: #0ea5e9;
265
- --shadow: 0 8px 24px rgba(0,0,0,0.06);
 
 
 
 
266
  }
267
 
268
  body, .gradio-container {
269
- background: var(--bg);
 
 
 
270
  }
271
 
272
  .page-wrap {
273
  padding: 6px 4px 30px 4px;
274
  }
275
 
276
- /* ================= HERO ================= */
277
  .hero {
278
  display: flex;
279
  align-items: center;
280
  justify-content: space-between;
281
  gap: 24px;
282
- background: linear-gradient(135deg, #e8f0ff, #f0f9ff);
283
  border: 1px solid var(--border);
284
- border-radius: 20px;
285
- padding: 24px;
286
- margin-bottom: 20px;
 
287
  }
288
 
289
  .eyebrow {
290
  color: var(--accent-2);
291
  font-size: 12px;
292
  font-weight: 700;
293
- letter-spacing: 0.1em;
294
  text-transform: uppercase;
 
295
  }
296
 
297
  .hero h1 {
298
  margin: 0;
299
  color: var(--text);
300
- font-size: 32px;
 
301
  }
302
 
303
  .hero p {
304
- margin: 8px 0 0 0;
305
  color: var(--muted);
 
306
  }
307
 
308
  .hero-stat {
309
- min-width: 120px;
310
  text-align: center;
311
- background: white;
312
  border: 1px solid var(--border);
313
- border-radius: 14px;
314
- padding: 16px;
315
  }
316
 
317
  .stat-num {
318
- font-size: 30px;
319
- font-weight: 700;
320
  color: var(--text);
 
 
321
  }
322
 
323
  .stat-label {
324
  color: var(--muted);
325
- font-size: 12px;
 
 
 
 
 
 
326
  }
327
 
328
- /* ================= CARD ================= */
329
  .story-card {
330
- background: white;
331
  border: 1px solid var(--border);
332
- border-radius: 20px;
333
- padding: 20px;
334
  box-shadow: var(--shadow);
 
335
  }
336
 
337
  .story-header {
338
  display: flex;
339
  justify-content: space-between;
340
- margin-bottom: 16px;
 
 
341
  }
342
 
343
  .story-header h2 {
344
  margin: 0;
345
  color: var(--text);
 
 
346
  }
347
 
348
  .story-sub {
349
  color: var(--muted);
350
- font-size: 12px;
 
 
351
  }
352
 
353
  .story-meta {
354
  display: flex;
355
- gap: 6px;
356
  flex-wrap: wrap;
 
 
357
  }
358
 
359
  .meta-pill {
360
- background: #eef2ff;
361
- border: 1px solid #dbeafe;
362
- color: #1e3a8a;
 
 
 
 
363
  border-radius: 999px;
364
- padding: 6px 10px;
365
- font-size: 11px;
 
 
 
 
 
366
  }
367
 
368
- /* ================= VIDEO ================= */
369
  .story-video {
370
  width: 100%;
371
- border-radius: 14px;
 
 
372
  border: 1px solid var(--border);
373
- margin-bottom: 16px;
374
  }
375
 
376
- /* ================= CONTENT ================= */
377
  .story-content {
378
  display: grid;
379
- grid-template-columns: 1.2fr 0.8fr;
380
- gap: 16px;
381
  }
382
 
383
  .story-text,
384
  .story-visuals {
385
- background: var(--panel-2);
386
  border: 1px solid var(--border);
387
- border-radius: 14px;
388
- padding: 16px;
389
  }
390
 
391
  .story-text h3,
392
  .story-visuals h3 {
393
- margin: 0 0 10px 0;
 
 
394
  }
395
 
396
  .story-markdown {
397
  color: var(--text);
398
- line-height: 1.6;
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  }
400
 
401
- /* ================= IMAGES ================= */
402
  .story-gallery {
403
  display: grid;
404
- grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
405
- gap: 10px;
 
 
 
 
 
406
  }
407
 
408
  .story-image {
409
  width: 100%;
410
- height: 140px;
411
  object-fit: cover;
412
- border-radius: 12px;
413
  border: 1px solid var(--border);
414
- transition: 0.2s;
 
415
  }
416
 
417
  .story-image:hover {
418
- transform: scale(1.03);
 
 
 
 
 
 
 
 
 
 
419
  }
420
 
421
- /* ================= MOBILE ================= */
422
- @media (max-width: 900px) {
423
  .story-content {
424
  grid-template-columns: 1fr;
425
  }
426
 
 
 
 
 
 
 
 
 
427
  .hero {
428
  flex-direction: column;
429
  align-items: flex-start;
@@ -432,12 +520,11 @@ body, .gradio-container {
432
  """
433
 
434
 
435
-
436
  # =========================================================
437
  # UI
438
  # =========================================================
439
  with gr.Blocks(
440
- title="Story Factory Showcase",
441
  theme=gr.themes.Soft(),
442
  css=CUSTOM_CSS,
443
  ) as demo:
 
156
 
157
  def make_story_card(story: dict) -> str:
158
  title = html.escape(story["title"])
159
+ subtitle = html.escape(story["folder"])
160
+ created = html.escape(format_ts(story["timestamp"]))
 
161
 
162
  story_md = download_markdown(story["markdown_path"])
163
  story_md_html = gr.Markdown().postprocess(story_md)
164
 
 
165
  video_html = ""
166
  if story["video_path"]:
167
  video_url = build_resolve_url(story["video_path"])
168
  video_html = f"""
169
+ <div class="story-video-wrap">
170
+ <video class="story-video" controls preload="metadata">
171
+ <source src="{video_url}" type="video/mp4">
172
+ Your browser does not support the video tag.
173
+ </video>
174
+ </div>
175
  """
176
 
 
177
  gallery_html = ""
178
  if story["image_paths"]:
179
  imgs = []
180
  for img_path in story["image_paths"]:
181
  img_url = build_resolve_url(img_path)
182
+ img_name = html.escape(os.path.basename(img_path))
183
  imgs.append(
184
+ f"""
185
+ <a href="{img_url}" target="_blank" class="story-image-link">
186
+ <img src="{img_url}" alt="{img_name}" class="story-image" loading="lazy">
187
+ </a>
188
+ """
189
  )
190
  gallery_html = f"""
191
  <div class="story-gallery">
 
193
  </div>
194
  """
195
 
196
+ dataset_badge = ""
197
+ if story["csv_path"]:
198
+ csv_url = build_resolve_url(story["csv_path"])
199
+ dataset_badge = (
200
+ f'<a class="meta-pill" href="{csv_url}" target="_blank">dataset.csv</a>'
201
+ )
202
+
203
  return f"""
204
  <section class="story-card">
205
+ <div class="story-header">
206
+ <div>
207
+ <h2>{title}</h2>
208
+ <div class="story-sub">{subtitle}</div>
209
+ </div>
210
+ <div class="story-meta">
211
+ <span class="meta-pill">{created}</span>
212
+ {dataset_badge}
213
+ <span class="meta-pill">{len(story["image_paths"])} images</span>
214
+ </div>
215
+ </div>
216
 
217
  {video_html}
218
 
219
  <div class="story-content">
220
  <div class="story-text">
221
+ <h3>Story</h3>
222
+ <div class="story-markdown">{story_md_html}</div>
223
  </div>
224
 
225
  <div class="story-visuals">
226
+ <h3>Images</h3>
227
+ {gallery_html or '<div class="empty-box">No images found.</div>'}
228
  </div>
229
  </div>
 
230
  </section>
231
  """
232
+
233
+
234
  def build_showcase_html():
235
  stories = collect_stories()
236
 
 
249
  <div class="hero">
250
  <div>
251
  <div class="eyebrow">Story Showcase</div>
252
+ <h1>LTX Story Factory</h1>
253
  <p>Latest generated stories, images, and videos from the dataset repo.</p>
254
  </div>
255
  <div class="hero-stat">
 
274
  # =========================================================
275
  CUSTOM_CSS = """
276
  :root {
277
+ --bg: #0b1020;
278
+ --panel: #121a2b;
279
+ --panel-2: #182339;
280
+ --border: rgba(255,255,255,0.08);
281
+ --text: #eef2ff;
282
+ --muted: #a9b4d0;
283
+ --accent: #7c9cff;
284
+ --accent-2: #90e0ef;
285
+ --shadow: 0 10px 30px rgba(0,0,0,0.25);
286
+ }
287
+
288
+ .gradio-container {
289
+ max-width: 1500px !important;
290
  }
291
 
292
  body, .gradio-container {
293
+ background:
294
+ radial-gradient(circle at top left, rgba(124,156,255,0.16), transparent 25%),
295
+ radial-gradient(circle at top right, rgba(144,224,239,0.10), transparent 20%),
296
+ linear-gradient(180deg, #0a0f1d, #0e1422 45%, #0b1020);
297
  }
298
 
299
  .page-wrap {
300
  padding: 6px 4px 30px 4px;
301
  }
302
 
 
303
  .hero {
304
  display: flex;
305
  align-items: center;
306
  justify-content: space-between;
307
  gap: 24px;
308
+ background: linear-gradient(135deg, rgba(124,156,255,0.18), rgba(144,224,239,0.10));
309
  border: 1px solid var(--border);
310
+ border-radius: 24px;
311
+ padding: 28px;
312
+ margin-bottom: 24px;
313
+ box-shadow: var(--shadow);
314
  }
315
 
316
  .eyebrow {
317
  color: var(--accent-2);
318
  font-size: 12px;
319
  font-weight: 700;
320
+ letter-spacing: 0.12em;
321
  text-transform: uppercase;
322
+ margin-bottom: 8px;
323
  }
324
 
325
  .hero h1 {
326
  margin: 0;
327
  color: var(--text);
328
+ font-size: 36px;
329
+ line-height: 1.1;
330
  }
331
 
332
  .hero p {
333
+ margin: 10px 0 0 0;
334
  color: var(--muted);
335
+ font-size: 16px;
336
  }
337
 
338
  .hero-stat {
339
+ min-width: 130px;
340
  text-align: center;
341
+ background: rgba(255,255,255,0.04);
342
  border: 1px solid var(--border);
343
+ border-radius: 20px;
344
+ padding: 18px;
345
  }
346
 
347
  .stat-num {
 
 
348
  color: var(--text);
349
+ font-size: 34px;
350
+ font-weight: 800;
351
  }
352
 
353
  .stat-label {
354
  color: var(--muted);
355
+ font-size: 13px;
356
+ }
357
+
358
+ .stories-list {
359
+ display: flex;
360
+ flex-direction: column;
361
+ gap: 22px;
362
  }
363
 
 
364
  .story-card {
365
+ background: linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0.015));
366
  border: 1px solid var(--border);
367
+ border-radius: 24px;
368
+ padding: 22px;
369
  box-shadow: var(--shadow);
370
+ backdrop-filter: blur(8px);
371
  }
372
 
373
  .story-header {
374
  display: flex;
375
  justify-content: space-between;
376
+ align-items: flex-start;
377
+ gap: 18px;
378
+ margin-bottom: 18px;
379
  }
380
 
381
  .story-header h2 {
382
  margin: 0;
383
  color: var(--text);
384
+ font-size: 28px;
385
+ line-height: 1.2;
386
  }
387
 
388
  .story-sub {
389
  color: var(--muted);
390
+ font-size: 13px;
391
+ margin-top: 6px;
392
+ word-break: break-word;
393
  }
394
 
395
  .story-meta {
396
  display: flex;
 
397
  flex-wrap: wrap;
398
+ gap: 8px;
399
+ justify-content: flex-end;
400
  }
401
 
402
  .meta-pill {
403
+ display: inline-flex;
404
+ align-items: center;
405
+ justify-content: center;
406
+ text-decoration: none;
407
+ color: var(--text);
408
+ background: rgba(124,156,255,0.12);
409
+ border: 1px solid rgba(124,156,255,0.25);
410
  border-radius: 999px;
411
+ padding: 8px 12px;
412
+ font-size: 12px;
413
+ font-weight: 600;
414
+ }
415
+
416
+ .story-video-wrap {
417
+ margin-bottom: 20px;
418
  }
419
 
 
420
  .story-video {
421
  width: 100%;
422
+ max-height: 580px;
423
+ border-radius: 18px;
424
+ background: #000;
425
  border: 1px solid var(--border);
 
426
  }
427
 
 
428
  .story-content {
429
  display: grid;
430
+ grid-template-columns: 1.15fr 0.85fr;
431
+ gap: 20px;
432
  }
433
 
434
  .story-text,
435
  .story-visuals {
436
+ background: rgba(255,255,255,0.025);
437
  border: 1px solid var(--border);
438
+ border-radius: 20px;
439
+ padding: 18px;
440
  }
441
 
442
  .story-text h3,
443
  .story-visuals h3 {
444
+ margin: 0 0 14px 0;
445
+ color: var(--text);
446
+ font-size: 18px;
447
  }
448
 
449
  .story-markdown {
450
  color: var(--text);
451
+ line-height: 1.7;
452
+ font-size: 15px;
453
+ }
454
+
455
+ .story-markdown p,
456
+ .story-markdown li,
457
+ .story-markdown h1,
458
+ .story-markdown h2,
459
+ .story-markdown h3,
460
+ .story-markdown h4,
461
+ .story-markdown h5,
462
+ .story-markdown h6,
463
+ .story-markdown blockquote {
464
+ color: var(--text) !important;
465
  }
466
 
 
467
  .story-gallery {
468
  display: grid;
469
+ grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
470
+ gap: 12px;
471
+ }
472
+
473
+ .story-image-link {
474
+ display: block;
475
+ text-decoration: none;
476
  }
477
 
478
  .story-image {
479
  width: 100%;
480
+ height: 180px;
481
  object-fit: cover;
482
+ border-radius: 16px;
483
  border: 1px solid var(--border);
484
+ transition: transform 0.18s ease, box-shadow 0.18s ease;
485
+ background: rgba(255,255,255,0.02);
486
  }
487
 
488
  .story-image:hover {
489
+ transform: translateY(-2px);
490
+ box-shadow: 0 10px 22px rgba(0,0,0,0.22);
491
+ }
492
+
493
+ .empty-box,
494
+ .empty-state {
495
+ background: rgba(255,255,255,0.025);
496
+ border: 1px dashed var(--border);
497
+ border-radius: 18px;
498
+ padding: 22px;
499
+ color: var(--muted);
500
  }
501
 
502
+ @media (max-width: 1100px) {
 
503
  .story-content {
504
  grid-template-columns: 1fr;
505
  }
506
 
507
+ .story-header {
508
+ flex-direction: column;
509
+ }
510
+
511
+ .story-meta {
512
+ justify-content: flex-start;
513
+ }
514
+
515
  .hero {
516
  flex-direction: column;
517
  align-items: flex-start;
 
520
  """
521
 
522
 
 
523
  # =========================================================
524
  # UI
525
  # =========================================================
526
  with gr.Blocks(
527
+ title="LTX Story Factory Showcase",
528
  theme=gr.themes.Soft(),
529
  css=CUSTOM_CSS,
530
  ) as demo: