Commit e303b2e4fa00b5446b162a90f0d053e174183ab1

Authored by yibo
1 parent c9009a54

Restyle live channel to infiniteslop.ai's comic-panel look, add queue upvoting

Reworks live.css/live.html to match infiniteslop.ai's visual language: Tahoma
bold type, pure-black canvas, hot-pink accent, and hand-drawn white cards with
a thick black border + hard offset shadow for every button/bubble/prompt card.
Left queue now groups into 接下来播放/生成中/排队中 sections, the center gets a
white "now playing" card (time + requester + prompt) plus a pulsing LIVE pill,
chat bubbles are colored per-nickname via a pastel hash, and the top bar gained
a chat-close toggle alongside mute and the watching count.

Adds real upvoting to go with the new queue UI: POST /api/queue/vote/:id
toggles a per-viewer (cookie uid) vote on a queued prompt, and pickNextClip
now schedules the highest-voted matched/ready entry first instead of strict
FIFO — lets chat bump a favorite prompt to the front of the line.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0144MiJ2cLjMd9jWaeEpVfUJ
library.json
1 1 [
2 2 {
  3 + "id": "4af4c61b64a6",
  4 + "source": "h3console",
  5 + "prompt": "一只白猫坐着,镜头缓慢推进,最终定格",
  6 + "fullPrompt": "一只白猫坐着,镜头缓慢推进,最终定格",
  7 + "edited": false,
  8 + "width": 832,
  9 + "height": 480,
  10 + "duration": 1,
  11 + "partition": "fl2va",
  12 + "likes": 0,
  13 + "createdAt": 1788251288958
  14 + },
  15 + {
3 16 "id": "8024972d6019",
4 17 "source": "h3console",
5 18 "prompt": "蛤蟆文太跳出来了打架",
... ... @@ -51,7 +64,7 @@
51 64 "likes": 0,
52 65 "createdAt": 1788192916199,
53 66 "promptEn": "taoist eyes the old house suspiciously",
54   - "lastPlayedAt": 1788242974304
  67 + "lastPlayedAt": 1788253021349
55 68 },
56 69 {
57 70 "id": "cb5baad5d724",
... ... @@ -66,7 +79,7 @@
66 79 "likes": 0,
67 80 "createdAt": 1788192915845,
68 81 "promptEn": "scholar watches the room warily",
69   - "lastPlayedAt": 1788249895446
  82 + "lastPlayedAt": 1788251736825
70 83 },
71 84 {
72 85 "id": "e8cbb73560ef",
... ... @@ -81,7 +94,7 @@
81 94 "likes": 0,
82 95 "createdAt": 1788192915549,
83 96 "promptEn": "taoist steels himself to exorcise something",
84   - "lastPlayedAt": 1788246217066
  97 + "lastPlayedAt": 1788252086979
85 98 },
86 99 {
87 100 "id": "5b6dcb357792",
... ... @@ -96,7 +109,7 @@
96 109 "likes": 0,
97 110 "createdAt": 1788192915268,
98 111 "promptEn": "scholar softens up, starts to trust",
99   - "lastPlayedAt": 1788249389537
  112 + "lastPlayedAt": 1788250818261
100 113 },
101 114 {
102 115 "id": "0c7565a5c8e8",
... ... @@ -111,7 +124,7 @@
111 124 "likes": 0,
112 125 "createdAt": 1788192914949,
113 126 "promptEn": "taoist acts tough but is nervous",
114   - "lastPlayedAt": 1788247784361
  127 + "lastPlayedAt": 1788252613211
115 128 },
116 129 {
117 130 "id": "167ce1bfa285",
... ... @@ -126,7 +139,7 @@
126 139 "likes": 0,
127 140 "createdAt": 1788192914047,
128 141 "promptEn": "scholar looks scared, wants to flee",
129   - "lastPlayedAt": 1788247259915
  142 + "lastPlayedAt": 1788252155012
130 143 },
131 144 {
132 145 "id": "053bc957b4f8",
... ... @@ -141,7 +154,7 @@
141 154 "likes": 0,
142 155 "createdAt": 1788192913886,
143 156 "promptEn": "taoist panics, freaked out",
144   - "lastPlayedAt": 1788247778362
  157 + "lastPlayedAt": 1788250312031
145 158 },
146 159 {
147 160 "id": "45c1136d1e05",
... ... @@ -156,7 +169,7 @@
156 169 "likes": 0,
157 170 "createdAt": 1788192913551,
158 171 "promptEn": "taoist looking panicked and scared",
159   - "lastPlayedAt": 1788249177441
  172 + "lastPlayedAt": 1788252637227
160 173 },
161 174 {
162 175 "id": "d169d42a67de",
... ... @@ -171,7 +184,7 @@
171 184 "likes": 0,
172 185 "createdAt": 1788192913492,
173 186 "promptEn": "scholar defiant out of desperation",
174   - "lastPlayedAt": 1788248821262
  187 + "lastPlayedAt": 1788252431138
175 188 },
176 189 {
177 190 "id": "2abd2793fae9",
... ... @@ -186,7 +199,7 @@
186 199 "likes": 0,
187 200 "createdAt": 1788192913244,
188 201 "promptEn": "scholar cornered but still defiant",
189   - "lastPlayedAt": 1788248124656
  202 + "lastPlayedAt": 1788250669748
190 203 },
191 204 {
192 205 "id": "bbf7efb467e0",
... ... @@ -201,7 +214,7 @@
201 214 "likes": 0,
202 215 "createdAt": 1788192912912,
203 216 "promptEn": "taoist charmed, hesitating",
204   - "lastPlayedAt": 1788249011362
  217 + "lastPlayedAt": 1788251760826
205 218 },
206 219 {
207 220 "id": "f8da9934c823",
... ... @@ -216,7 +229,7 @@
216 229 "likes": 0,
217 230 "createdAt": 1788192912819,
218 231 "promptEn": "scholar dazed, charmed",
219   - "lastPlayedAt": 1788249063385
  232 + "lastPlayedAt": 1788252975330
220 233 },
221 234 {
222 235 "id": "feb96d5be50f",
... ... @@ -231,7 +244,7 @@
231 244 "likes": 0,
232 245 "createdAt": 1788191432903,
233 246 "promptEn": "woman in white startled, a bit shy",
234   - "lastPlayedAt": 1788247175839
  247 + "lastPlayedAt": 1788252189029
235 248 },
236 249 {
237 250 "id": "14f482bdae7f",
... ... @@ -246,7 +259,7 @@
246 259 "likes": 0,
247 260 "createdAt": 1788191432362,
248 261 "promptEn": "woman in white being intimate and alluring",
249   - "lastPlayedAt": 1788247648262
  262 + "lastPlayedAt": 1788251702786
250 263 },
251 264 {
252 265 "id": "362b15ab1811",
... ... @@ -261,7 +274,7 @@
261 274 "likes": 0,
262 275 "createdAt": 1788191431874,
263 276 "promptEn": "woman in white gently comforting someone",
264   - "lastPlayedAt": 1788245286273
  277 + "lastPlayedAt": 1788251802847
265 278 },
266 279 {
267 280 "id": "a2c515055269",
... ... @@ -276,7 +289,7 @@
276 289 "likes": 0,
277 290 "createdAt": 1788191431505,
278 291 "promptEn": "woman in white scared, dodging the question",
279   - "lastPlayedAt": 1788248629102
  292 + "lastPlayedAt": 1788252941314
280 293 },
281 294 {
282 295 "id": "667978d52305",
... ... @@ -291,7 +304,7 @@
291 304 "likes": 0,
292 305 "createdAt": 1788191431016,
293 306 "promptEn": "woman in white coldly threatening",
294   - "lastPlayedAt": 1788249305499
  307 + "lastPlayedAt": 1788251764831
295 308 },
296 309 {
297 310 "id": "7f5213748d87",
... ... @@ -306,7 +319,7 @@
306 319 "likes": 0,
307 320 "createdAt": 1788188423583,
308 321 "promptEn": "woman in white scared, dodging",
309   - "lastPlayedAt": 1788244191373
  322 + "lastPlayedAt": 1788251198437
310 323 },
311 324 {
312 325 "id": "1597db4be561",
... ... @@ -320,7 +333,7 @@
320 333 "partition": "ref2va",
321 334 "likes": 0,
322 335 "createdAt": 1788187365789,
323   - "lastPlayedAt": 1788249805778,
  336 + "lastPlayedAt": 1788251380546,
324 337 "promptEn": "woman in white being gentle and comforting"
325 338 },
326 339 {
... ... @@ -335,7 +348,7 @@
335 348 "partition": "ref2va",
336 349 "likes": 0,
337 350 "createdAt": 1788186218619,
338   - "lastPlayedAt": 1788248797247,
  351 + "lastPlayedAt": 1788252471153,
339 352 "promptEn": "woman in white evading, scared"
340 353 },
341 354 {
... ... @@ -351,7 +364,7 @@
351 364 "likes": 0,
352 365 "createdAt": 1788185090875,
353 366 "promptEn": "woman in white cold and threatening",
354   - "lastPlayedAt": 1788249227469
  367 + "lastPlayedAt": 1788251808852
355 368 },
356 369 {
357 370 "id": "a46f1daa9ee0",
... ... @@ -366,7 +379,7 @@
366 379 "likes": 0,
367 380 "createdAt": 1788181569491,
368 381 "promptEn": "a massive warship glides through the stars, the bridge suddenly shakes hard",
369   - "lastPlayedAt": 1788244309493
  382 + "lastPlayedAt": 1788251214448
370 383 },
371 384 {
372 385 "id": "44bf5b969e94",
... ... @@ -381,7 +394,7 @@
381 394 "likes": 0,
382 395 "createdAt": 1788162094002,
383 396 "promptEn": "woman in old chinese robe talking, gentle but wary, in an old room",
384   - "lastPlayedAt": 1788248591076
  397 + "lastPlayedAt": 1788250690213
385 398 },
386 399 {
387 400 "id": "e17cfe57f56a",
... ... @@ -396,7 +409,7 @@
396 409 "likes": 0,
397 410 "createdAt": 1788160917846,
398 411 "promptEn": "woman in old chinese robe talking, gentle but wary, in an old room",
399   - "lastPlayedAt": 1788248635111
  412 + "lastPlayedAt": 1788251177165
400 413 },
401 414 {
402 415 "id": "24e49e5ae069",
... ... @@ -411,7 +424,7 @@
411 424 "likes": 0,
412 425 "createdAt": 1788159367556,
413 426 "promptEn": "constable talking nervously in an old room, looking scared",
414   - "lastPlayedAt": 1788249213459
  427 + "lastPlayedAt": 1788252509170
415 428 },
416 429 {
417 430 "id": "a6d767413426",
... ... @@ -426,7 +439,7 @@
426 439 "likes": 0,
427 440 "createdAt": 1788157322611,
428 441 "promptEn": "constable talking, looking scared",
429   - "lastPlayedAt": 1788244543682
  442 + "lastPlayedAt": 1788250916299
430 443 },
431 444 {
432 445 "id": "7977eeeabe8b",
... ... @@ -441,7 +454,7 @@
441 454 "likes": 0,
442 455 "createdAt": 1788157177505,
443 456 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen",
444   - "lastPlayedAt": 1788249173441
  457 + "lastPlayedAt": 1788250622191
445 458 },
446 459 {
447 460 "id": "a9c4ba5e453b",
... ... @@ -456,7 +469,7 @@
456 469 "likes": 0,
457 470 "createdAt": 1788156878022,
458 471 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen",
459   - "lastPlayedAt": 1788245662609
  472 + "lastPlayedAt": 1788252225046
460 473 },
461 474 {
462 475 "id": "a0b752e7ac0d",
... ... @@ -471,7 +484,7 @@
471 484 "likes": 0,
472 485 "createdAt": 1788154942596,
473 486 "promptEn": "constable talking, looking scared",
474   - "lastPlayedAt": 1788247279933
  487 + "lastPlayedAt": 1788252145004
475 488 },
476 489 {
477 490 "id": "b6608e2eac60",
... ... @@ -486,7 +499,7 @@
486 499 "likes": 0,
487 500 "createdAt": 1788153988474,
488 501 "promptEn": "astronaut at a martian canyon, visor reflecting alien ruins, then aerial view of the city",
489   - "lastPlayedAt": 1788248997353
  502 + "lastPlayedAt": 1788251428589
490 503 },
491 504 {
492 505 "id": "194fc2b56cb5",
... ... @@ -501,7 +514,7 @@
501 514 "likes": 0,
502 515 "createdAt": 1788138492364,
503 516 "promptEn": "astronaut on a Martian canyon, helmet reflecting an alien ruined city",
504   - "lastPlayedAt": 1788250133927
  517 + "lastPlayedAt": 1788251558701
505 518 },
506 519 {
507 520 "id": "f84b8f044ec5",
... ... @@ -531,7 +544,7 @@
531 544 "likes": 0,
532 545 "createdAt": 1788120171608,
533 546 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen",
534   - "lastPlayedAt": 1788248811254
  547 + "lastPlayedAt": 1788252411133
535 548 },
536 549 {
537 550 "id": "b8c762a3672e",
... ... @@ -561,7 +574,7 @@
561 574 "likes": 0,
562 575 "createdAt": 1788085282201,
563 576 "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen",
564   - "lastPlayedAt": 1788248290798
  577 + "lastPlayedAt": 1788251288482
565 578 },
566 579 {
567 580 "id": "683ab2fd4681",
... ... @@ -576,7 +589,7 @@
576 589 "likes": 0,
577 590 "createdAt": 1788071067702,
578 591 "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen",
579   - "lastPlayedAt": 1788249939841
  592 + "lastPlayedAt": 1788251524674
580 593 },
581 594 {
582 595 "id": "2e09fcd3d42c",
... ... @@ -591,7 +604,7 @@
591 604 "likes": 0,
592 605 "createdAt": 1788062322252,
593 606 "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen",
594   - "lastPlayedAt": 1788247317966
  607 + "lastPlayedAt": 1788252491166
595 608 },
596 609 {
597 610 "id": "12ea9ce00f36",
... ... @@ -606,7 +619,7 @@
606 619 "likes": 0,
607 620 "createdAt": 1788039783070,
608 621 "promptEn": "silver-haired idol backstage, red light pulsing, offering a blood-red contract",
609   - "lastPlayedAt": 1788247167832
  622 + "lastPlayedAt": 1788251726803
610 623 },
611 624 {
612 625 "id": "75bdbacd3012",
... ... @@ -621,7 +634,7 @@
621 634 "likes": 0,
622 635 "createdAt": 1788039782992,
623 636 "promptEn": "silver-haired idol backstage, red light pulsing, offering a blood-red contract",
624   - "lastPlayedAt": 1788249243476
  637 + "lastPlayedAt": 1788250966325
625 638 },
626 639 {
627 640 "id": "69e75a6b3997",
... ... @@ -636,7 +649,7 @@
636 649 "likes": 0,
637 650 "createdAt": 1788027833836,
638 651 "promptEn": "wounded hunter at a ruined subway dungeon entrance, lightning flickers, pointing into the tunnel",
639   - "lastPlayedAt": 1788248889310
  652 + "lastPlayedAt": 1788250480118
640 653 },
641 654 {
642 655 "id": "cde399c27dc4",
... ... @@ -651,7 +664,7 @@
651 664 "likes": 0,
652 665 "createdAt": 1788026877547,
653 666 "promptEn": "a deity in gold crown and red veil, quietly judging you in a moonlit bridal chamber",
654   - "lastPlayedAt": 1788249765707
  667 + "lastPlayedAt": 1788252887292
655 668 },
656 669 {
657 670 "id": "d5f811737e95",
... ... @@ -666,7 +679,7 @@
666 679 "likes": 0,
667 680 "createdAt": 1788026876886,
668 681 "promptEn": "apartment manager in an eerie corridor, red elevator light flickering, holding out a key",
669   - "lastPlayedAt": 1788245048064
  682 + "lastPlayedAt": 1788250332049
670 683 },
671 684 {
672 685 "id": "cdd03cca8461",
... ... @@ -681,7 +694,7 @@
681 694 "likes": 0,
682 695 "createdAt": 1788026569422,
683 696 "promptEn": "stitched-face long-haired curse on a subway platform, flickering lights, cruel smile",
684   - "lastPlayedAt": 1788247712313
  697 + "lastPlayedAt": 1788253043365
685 698 },
686 699 {
687 700 "id": "b8dd0f62f648",
... ... @@ -696,7 +709,7 @@
696 709 "likes": 0,
697 710 "createdAt": 1788026569123,
698 711 "promptEn": "pale dream demon in a suit, reaching out a hand inside a dream train car",
699   - "lastPlayedAt": 1788250129929
  712 + "lastPlayedAt": 1788252102983
700 713 },
701 714 {
702 715 "id": "bd6e2b264243",
... ... @@ -711,7 +724,7 @@
711 724 "likes": 0,
712 725 "createdAt": 1788026568815,
713 726 "promptEn": "blond soldier in combat gear, bracing at the entrance of a ruined basement battlefield",
714   - "lastPlayedAt": 1788249067384
  727 + "lastPlayedAt": 1788252839269
715 728 },
716 729 {
717 730 "id": "b0d6af33eace",
... ... @@ -726,7 +739,7 @@
726 739 "likes": 0,
727 740 "createdAt": 1788026568516,
728 741 "promptEn": "orange-haired villain in a black cloak with red clouds, raising a hand in a ruined village",
729   - "lastPlayedAt": 1788248893311
  742 + "lastPlayedAt": 1788251692781
730 743 },
731 744 {
732 745 "id": "b077fd090415",
... ... @@ -741,7 +754,7 @@
741 754 "likes": 0,
742 755 "createdAt": 1788026568242,
743 756 "promptEn": "magma commander in a white coat, raising a lava fist on the battlefield",
744   - "lastPlayedAt": 1788248741209
  757 + "lastPlayedAt": 1788252487163
745 758 },
746 759 {
747 760 "id": "1442013e94f0",
... ... @@ -756,7 +769,7 @@
756 769 "likes": 0,
757 770 "createdAt": 1788026068497,
758 771 "promptEn": "black-haired prince in a red cloak, pressing down an execution order in the trial hall",
759   - "lastPlayedAt": 1788249833797
  772 + "lastPlayedAt": 1788251740816
760 773 },
761 774 {
762 775 "id": "0a5e37bb328c",
... ... @@ -771,7 +784,7 @@
771 784 "likes": 0,
772 785 "createdAt": 1788025642732,
773 786 "promptEn": "Vecna reaching out a claw in his lair in the Upside Down, red lightning",
774   - "lastPlayedAt": 1788249929837
  787 + "lastPlayedAt": 1788252559192
775 788 },
776 789 {
777 790 "id": "00862f966c3f",
... ... @@ -786,7 +799,7 @@
786 799 "likes": 0,
787 800 "createdAt": 1788025640972,
788 801 "promptEn": "Voldemort in a graveyard, wand raised, stepping closer",
789   - "lastPlayedAt": 1788249433560
  802 + "lastPlayedAt": 1788252161011
790 803 },
791 804 {
792 805 "id": "c3875fbd6cc4",
... ... @@ -801,7 +814,7 @@
801 814 "likes": 0,
802 815 "createdAt": 1788025632028,
803 816 "promptEn": "a giant mouth embedded in the wall at the end of an endless hotel corridor, slowly opening",
804   - "lastPlayedAt": 1788246707462
  817 + "lastPlayedAt": 1788252701202
805 818 },
806 819 {
807 820 "id": "1c6e2df243f4",
... ... @@ -816,7 +829,7 @@
816 829 "likes": 0,
817 830 "createdAt": 1788025630249,
818 831 "promptEn": "white-haired villain in a ruined street at night, cracked smile, reaching out a hand",
819   - "lastPlayedAt": 1788248865293
  832 + "lastPlayedAt": 1788251462624
820 833 },
821 834 {
822 835 "id": "d735f1fc010f",
... ... @@ -831,7 +844,7 @@
831 844 "likes": 0,
832 845 "createdAt": 1788025624377,
833 846 "promptEn": "cat-eared white-haired guard crouching in a ruined palace, purple aura around them",
834   - "lastPlayedAt": 1788244791899
  847 + "lastPlayedAt": 1788252499167
835 848 },
836 849 {
837 850 "id": "b16b64e52f83",
... ... @@ -846,7 +859,7 @@
846 859 "likes": 0,
847 860 "createdAt": 1788025623833,
848 861 "promptEn": "cat-eared white-haired guard crouching in a ruined palace, purple aura around them",
849   - "lastPlayedAt": 1788249503586
  862 + "lastPlayedAt": 1788251424585
850 863 },
851 864 {
852 865 "id": "0d721acbc298",
... ... @@ -861,7 +874,7 @@
861 874 "likes": 0,
862 875 "createdAt": 1788025622363,
863 876 "promptEn": "detective in a surveillance room squinting at the monitors",
864   - "lastPlayedAt": 1788247902462
  877 + "lastPlayedAt": 1788250159958
865 878 },
866 879 {
867 880 "id": "639069dea7c5",
... ... @@ -876,7 +889,7 @@
876 889 "likes": 0,
877 890 "createdAt": 1788025620717,
878 891 "promptEn": "alien tyrant holding a glowing energy sphere, sneering on a rocky green battlefield",
879   - "lastPlayedAt": 1788249089402
  892 + "lastPlayedAt": 1788252495168
880 893 },
881 894 {
882 895 "id": "9db41d807330",
... ... @@ -891,7 +904,7 @@
891 904 "likes": 0,
892 905 "createdAt": 1788025220954,
893 906 "promptEn": "pirate captain plants a cutlass on the table, making a dangerous bargain",
894   - "lastPlayedAt": 1788247468104
  907 + "lastPlayedAt": 1788251456618
895 908 },
896 909 {
897 910 "id": "3e03975ff40a",
... ... @@ -921,7 +934,7 @@
921 934 "likes": 0,
922 935 "createdAt": 1788024900966,
923 936 "promptEn": "player 101 blocking the path on a high glass bridge, threatening you",
924   - "lastPlayedAt": 1788248885312
  937 + "lastPlayedAt": 1788251722799
925 938 },
926 939 {
927 940 "id": "d6aab3fce63d",
... ... @@ -936,7 +949,7 @@
936 949 "likes": 0,
937 950 "createdAt": 1788024747209,
938 951 "promptEn": "wounded hunter leaning on a subway turnstile, ordering you into the tunnel",
939   - "lastPlayedAt": 1788248130662
  952 + "lastPlayedAt": 1788252002941
940 953 },
941 954 {
942 955 "id": "fdefca4e4980",
... ... @@ -951,7 +964,7 @@
951 964 "likes": 0,
952 965 "createdAt": 1788024491625,
953 966 "promptEn": "prince signs an execution order in the trial hall, points at the guillotine",
954   - "lastPlayedAt": 1788247143814
  967 + "lastPlayedAt": 1788251928912
955 968 },
956 969 {
957 970 "id": "8a933feb52c4",
... ... @@ -966,7 +979,7 @@
966 979 "likes": 0,
967 980 "createdAt": 1788019483999,
968 981 "promptEn": "constable rushes in, glances back at the door, then talks to the woman behind the screen",
969   - "lastPlayedAt": 1788249277490
  982 + "lastPlayedAt": 1788252139001
970 983 },
971 984 {
972 985 "id": "998e3e6f9a2f",
... ... @@ -981,7 +994,7 @@
981 994 "likes": 0,
982 995 "createdAt": 1788016668421,
983 996 "promptEn": "pirate negotiation, catching Barbossa's flaw and turning the tables",
984   - "lastPlayedAt": 1788248855284
  997 + "lastPlayedAt": 1788250676206
985 998 },
986 999 {
987 1000 "id": "e3b18b5dfd8e",
... ... @@ -996,7 +1009,7 @@
996 1009 "likes": 0,
997 1010 "createdAt": 1788016434846,
998 1011 "promptEn": "pirate negotiation, catching Barbossa's flaw and turning the tables",
999   - "lastPlayedAt": 1788241122721
  1012 + "lastPlayedAt": 1788251638752
1000 1013 },
1001 1014 {
1002 1015 "id": "03e32ff5f0ef",
... ... @@ -1011,7 +1024,7 @@
1011 1024 "likes": 0,
1012 1025 "createdAt": 1788016203093,
1013 1026 "promptEn": "pirate negotiation, held the goal but paid a price",
1014   - "lastPlayedAt": 1788248931331
  1027 + "lastPlayedAt": 1788250906296
1015 1028 },
1016 1029 {
1017 1030 "id": "18aa882cbd0b",
... ... @@ -1041,7 +1054,7 @@
1041 1054 "likes": 0,
1042 1055 "createdAt": 1788015737873,
1043 1056 "promptEn": "pirate negotiation, went in head-on and Barbossa saw it coming",
1044   - "lastPlayedAt": 1788245038056
  1057 + "lastPlayedAt": 1788252825264
1045 1058 },
1046 1059 {
1047 1060 "id": "2a5ee87b8f13",
... ... @@ -1056,7 +1069,7 @@
1056 1069 "likes": 0,
1057 1070 "createdAt": 1788015493445,
1058 1071 "promptEn": "pirate negotiation, went in head-on and Barbossa saw it coming",
1059   - "lastPlayedAt": 1788249283490
  1072 + "lastPlayedAt": 1788250790245
1060 1073 },
1061 1074 {
1062 1075 "id": "3f30f5097438",
... ... @@ -1071,7 +1084,7 @@
1071 1084 "likes": 0,
1072 1085 "createdAt": 1788015035234,
1073 1086 "promptEn": "pirate negotiation, stayed passive and got cornered",
1074   - "lastPlayedAt": 1788241330893
  1087 + "lastPlayedAt": 1788251632751
1075 1088 },
1076 1089 {
1077 1090 "id": "6a0d14cabe17",
... ... @@ -1086,7 +1099,7 @@
1086 1099 "likes": 0,
1087 1100 "createdAt": 1788014791733,
1088 1101 "promptEn": "pirate negotiation, stayed passive and got cornered",
1089   - "lastPlayedAt": 1788249325507
  1102 + "lastPlayedAt": 1788252169019
1090 1103 },
1091 1104 {
1092 1105 "id": "08120928cc77",
... ... @@ -1101,7 +1114,7 @@
1101 1114 "likes": 0,
1102 1115 "createdAt": 1788014790969,
1103 1116 "promptEn": "pirate negotiation, stayed passive and got cornered",
1104   - "lastPlayedAt": 1788248146678
  1117 + "lastPlayedAt": 1788251730808
1105 1118 },
1106 1119 {
1107 1120 "id": "e14fe7c3220e",
... ... @@ -1116,7 +1129,7 @@
1116 1129 "likes": 0,
1117 1130 "createdAt": 1788014563344,
1118 1131 "promptEn": "captain's cabin on the Black Pearl, surrounded by pirates, negotiation could turn into an execution",
1119   - "lastPlayedAt": 1788246847578
  1132 + "lastPlayedAt": 1788252563192
1120 1133 },
1121 1134 {
1122 1135 "id": "c6e8560f62a2",
... ... @@ -1131,7 +1144,7 @@
1131 1144 "likes": 0,
1132 1145 "createdAt": 1788014336515,
1133 1146 "promptEn": "captain's cabin on the Black Pearl, surrounded by pirates, negotiation could turn into an execution",
1134   - "lastPlayedAt": 1788245304286
  1147 + "lastPlayedAt": 1788250666207
1135 1148 },
1136 1149 {
1137 1150 "id": "eae105ee1204",
... ... @@ -1146,7 +1159,7 @@
1146 1159 "likes": 0,
1147 1160 "createdAt": 1788014099497,
1148 1161 "promptEn": "glass bridge game, exploiting a flaw to push back player 101",
1149   - "lastPlayedAt": 1788249885816
  1162 + "lastPlayedAt": 1788251098395
1150 1163 },
1151 1164 {
1152 1165 "id": "24576c264a25",
... ... @@ -1176,7 +1189,7 @@
1176 1189 "likes": 0,
1177 1190 "createdAt": 1788013634333,
1178 1191 "promptEn": "glass bridge game, held the goal but paid a price",
1179   - "lastPlayedAt": 1788247720319
  1192 + "lastPlayedAt": 1788252773238
1180 1193 },
1181 1194 {
1182 1195 "id": "5b8ef21616d4",
... ... @@ -1191,7 +1204,7 @@
1191 1204 "likes": 0,
1192 1205 "createdAt": 1788013632065,
1193 1206 "promptEn": "glass bridge game, held the goal but paid a price",
1194   - "lastPlayedAt": 1788249933837
  1207 + "lastPlayedAt": 1788252935312
1195 1208 },
1196 1209 {
1197 1210 "id": "892b923eed29",
... ... @@ -1221,7 +1234,7 @@
1221 1234 "likes": 0,
1222 1235 "createdAt": 1788013155592,
1223 1236 "promptEn": "glass bridge game, stayed passive and got cornered",
1224   - "lastPlayedAt": 1788242766130
  1237 + "lastPlayedAt": 1788252737220
1225 1238 },
1226 1239 {
1227 1240 "id": "f80c8ec1a8c8",
... ... @@ -1236,7 +1249,7 @@
1236 1249 "likes": 0,
1237 1250 "createdAt": 1788012917294,
1238 1251 "promptEn": "standing on the glass bridge, facing off with player 101",
1239   - "lastPlayedAt": 1788248120651
  1252 + "lastPlayedAt": 1788251642756
1240 1253 },
1241 1254 {
1242 1255 "id": "8f23de61904f",
... ... @@ -1251,7 +1264,7 @@
1251 1264 "likes": 0,
1252 1265 "createdAt": 1788012917185,
1253 1266 "promptEn": "standing on the glass bridge, facing off with player 101",
1254   - "lastPlayedAt": 1788244377528
  1267 + "lastPlayedAt": 1788250786246
1255 1268 },
1256 1269 {
1257 1270 "id": "bb4a8351d822",
... ... @@ -1281,7 +1294,7 @@
1281 1294 "likes": 0,
1282 1295 "createdAt": 1788012441458,
1283 1296 "promptEn": "facing Vecna, held the goal but paid a price",
1284   - "lastPlayedAt": 1788248951336
  1297 + "lastPlayedAt": 1788251798845
1285 1298 },
1286 1299 {
1287 1300 "id": "691c2f679759",
... ... @@ -1296,7 +1309,7 @@
1296 1309 "likes": 0,
1297 1310 "createdAt": 1788012202802,
1298 1311 "promptEn": "facing Vecna, went in head-on and got seen through",
1299   - "lastPlayedAt": 1788244267443
  1312 + "lastPlayedAt": 1788252096983
1300 1313 },
1301 1314 {
1302 1315 "id": "07f957783a09",
... ... @@ -1341,7 +1354,7 @@
1341 1354 "likes": 0,
1342 1355 "createdAt": 1788011727444,
1343 1356 "promptEn": "entering Vecna's mental lair, red lightning, twisted space",
1344   - "lastPlayedAt": 1788237488190
  1357 + "lastPlayedAt": 1788251520666
1345 1358 },
1346 1359 {
1347 1360 "id": "6a9af7e7368d",
... ... @@ -1356,7 +1369,7 @@
1356 1369 "likes": 0,
1357 1370 "createdAt": 1788011488775,
1358 1371 "promptEn": "graveyard standoff, exploiting a flaw to counter Voldemort",
1359   - "lastPlayedAt": 1788249467572
  1372 + "lastPlayedAt": 1788252403130
1360 1373 },
1361 1374 {
1362 1375 "id": "24845be71ea0",
... ... @@ -1371,7 +1384,7 @@
1371 1384 "likes": 0,
1372 1385 "createdAt": 1788011488667,
1373 1386 "promptEn": "graveyard standoff, exploiting a flaw to counter Voldemort",
1374   - "lastPlayedAt": 1788248432935
  1387 + "lastPlayedAt": 1788252803254
1375 1388 },
1376 1389 {
1377 1390 "id": "f1012a3d7aa7",
... ... @@ -1386,7 +1399,7 @@
1386 1399 "likes": 0,
1387 1400 "createdAt": 1788011251158,
1388 1401 "promptEn": "facing Voldemort, held the goal but paid a price",
1389   - "lastPlayedAt": 1788244998065
  1402 + "lastPlayedAt": 1788251207183
1390 1403 },
1391 1404 {
1392 1405 "id": "58f68b111429",
... ... @@ -1401,7 +1414,7 @@
1401 1414 "likes": 0,
1402 1415 "createdAt": 1788011013309,
1403 1416 "promptEn": "facing Voldemort, went in head-on and got seen through",
1404   - "lastPlayedAt": 1788245638583
  1417 + "lastPlayedAt": 1788252965327
1405 1418 },
1406 1419 {
1407 1420 "id": "682cbc166479",
... ... @@ -1416,7 +1429,7 @@
1416 1429 "likes": 0,
1417 1430 "createdAt": 1788010775613,
1418 1431 "promptEn": "facing Voldemort, stayed passive and got cornered",
1419   - "lastPlayedAt": 1788249909833
  1432 + "lastPlayedAt": 1788252633224
1420 1433 },
1421 1434 {
1422 1435 "id": "795842efa4a1",
... ... @@ -1446,7 +1459,7 @@
1446 1459 "likes": 0,
1447 1460 "createdAt": 1788010299688,
1448 1461 "promptEn": "facing Thanos, exploiting a flaw to counter him",
1449   - "lastPlayedAt": 1788249423555
  1462 + "lastPlayedAt": 1788251102950
1450 1463 },
1451 1464 {
1452 1465 "id": "469294a4ed09",
... ... @@ -1461,7 +1474,7 @@
1461 1474 "likes": 0,
1462 1475 "createdAt": 1788010062535,
1463 1476 "promptEn": "facing Thanos, held the goal but paid a price",
1464   - "lastPlayedAt": 1788247440076
  1477 + "lastPlayedAt": 1788252076972
1465 1478 },
1466 1479 {
1467 1480 "id": "324b84ebc607",
... ... @@ -1476,7 +1489,7 @@
1476 1489 "likes": 0,
1477 1490 "createdAt": 1788009825082,
1478 1491 "promptEn": "facing Thanos, went in head-on and got seen through",
1479   - "lastPlayedAt": 1788248991352
  1492 + "lastPlayedAt": 1788252393143
1480 1493 },
1481 1494 {
1482 1495 "id": "a6ead2cbe03f",
... ... @@ -1506,7 +1519,7 @@
1506 1519 "likes": 0,
1507 1520 "createdAt": 1788009346020,
1508 1521 "promptEn": "battlefield on Titan, the last chance to stop Thanos",
1509   - "lastPlayedAt": 1788246895620
  1522 + "lastPlayedAt": 1788252467159
1510 1523 },
1511 1524 {
1512 1525 "id": "66a96c6bc7cb",
... ... @@ -1521,7 +1534,7 @@
1521 1534 "likes": 0,
1522 1535 "createdAt": 1788009108302,
1523 1536 "promptEn": "in the endless hotel, exploiting a flaw to counter the Eternity Devil",
1524   - "lastPlayedAt": 1788249037369
  1537 + "lastPlayedAt": 1788252247056
1525 1538 },
1526 1539 {
1527 1540 "id": "71c590b0ea8f",
... ... @@ -1536,7 +1549,7 @@
1536 1549 "likes": 0,
1537 1550 "createdAt": 1788008870118,
1538 1551 "promptEn": "facing the Eternity Devil, held the goal but paid a price",
1539   - "lastPlayedAt": 1788245856782
  1552 + "lastPlayedAt": 1788252165013
1540 1553 },
1541 1554 {
1542 1555 "id": "35833c2cabb9",
... ... @@ -1551,7 +1564,7 @@
1551 1564 "likes": 0,
1552 1565 "createdAt": 1788008632603,
1553 1566 "promptEn": "facing the Eternity Devil, went in head-on and got seen through",
1554   - "lastPlayedAt": 1788248382886
  1567 + "lastPlayedAt": 1788250580165
1555 1568 },
1556 1569 {
1557 1570 "id": "361c80537652",
... ... @@ -1566,7 +1579,7 @@
1566 1579 "likes": 0,
1567 1580 "createdAt": 1788008394344,
1568 1581 "promptEn": "facing the Eternity Devil, stayed passive and got cornered",
1569   - "lastPlayedAt": 1788247716320
  1582 + "lastPlayedAt": 1788252833268
1570 1583 },
1571 1584 {
1572 1585 "id": "1a57de01bad0",
... ... @@ -1581,7 +1594,7 @@
1581 1594 "likes": 0,
1582 1595 "createdAt": 1788008156307,
1583 1596 "promptEn": "hotel floor sealed by the Eternity Devil, an endless looping corridor",
1584   - "lastPlayedAt": 1788248987350
  1597 + "lastPlayedAt": 1788251948919
1585 1598 },
1586 1599 {
1587 1600 "id": "cd3b6dda90ad",
... ... @@ -1596,7 +1609,7 @@
1596 1609 "likes": 0,
1597 1610 "createdAt": 1788007915610,
1598 1611 "promptEn": "in the ruined city, exploiting a flaw to counter Tomura",
1599   - "lastPlayedAt": 1788249919450
  1612 + "lastPlayedAt": 1788252961326
1600 1613 },
1601 1614 {
1602 1615 "id": "f67685cd6862",
... ... @@ -1611,7 +1624,7 @@
1611 1624 "likes": 0,
1612 1625 "createdAt": 1788007678723,
1613 1626 "promptEn": "facing Tomura, held the goal but paid a price",
1614   - "lastPlayedAt": 1788248458956
  1627 + "lastPlayedAt": 1788252219044
1615 1628 },
1616 1629 {
1617 1630 "id": "ee5bd111d8bd",
... ... @@ -1626,7 +1639,7 @@
1626 1639 "likes": 0,
1627 1640 "createdAt": 1788007441156,
1628 1641 "promptEn": "battlefield in Jaku city, the city in ruins, heroes in crisis",
1629   - "lastPlayedAt": 1788243420700
  1642 + "lastPlayedAt": 1788251770832
1630 1643 },
1631 1644 {
1632 1645 "id": "68219d6b68c8",
... ... @@ -1641,7 +1654,7 @@
1641 1654 "likes": 0,
1642 1655 "createdAt": 1788007201908,
1643 1656 "promptEn": "breaking into the palace, Neferpitou stalling to protect the king",
1644   - "lastPlayedAt": 1788249889814
  1657 + "lastPlayedAt": 1788252681196
1645 1658 },
1646 1659 {
1647 1660 "id": "628026375795",
... ... @@ -1656,7 +1669,7 @@
1656 1669 "likes": 0,
1657 1670 "createdAt": 1788006964122,
1658 1671 "promptEn": "grabbing an evidence flaw to counter L",
1659   - "lastPlayedAt": 1788249155428
  1672 + "lastPlayedAt": 1788251193178
1660 1673 },
1661 1674 {
1662 1675 "id": "0c6306b07f79",
... ... @@ -1671,7 +1684,7 @@
1671 1684 "likes": 0,
1672 1685 "createdAt": 1788006725887,
1673 1686 "promptEn": "facing off with L, went in head-on and got seen through",
1674   - "lastPlayedAt": 1788244141322
  1687 + "lastPlayedAt": 1788252617213
1675 1688 },
1676 1689 {
1677 1690 "id": "ee82188227fa",
... ... @@ -1686,7 +1699,7 @@
1686 1699 "likes": 0,
1687 1700 "createdAt": 1788006252918,
1688 1701 "promptEn": "facing L, stayed passive and the evidence pressure maxed out",
1689   - "lastPlayedAt": 1788247598221
  1702 + "lastPlayedAt": 1788251482641
1690 1703 },
1691 1704 {
1692 1705 "id": "0e28a7fecf03",
... ... @@ -1701,7 +1714,7 @@
1701 1714 "likes": 0,
1702 1715 "createdAt": 1788006252824,
1703 1716 "promptEn": "facing L, stayed passive and the evidence pressure maxed out",
1704   - "lastPlayedAt": 1788248392895
  1717 + "lastPlayedAt": 1788250153939
1705 1718 },
1706 1719 {
1707 1720 "id": "fb0738ad3e3b",
... ... @@ -1716,7 +1729,7 @@
1716 1729 "likes": 0,
1717 1730 "createdAt": 1788006015175,
1718 1731 "promptEn": "on the ruined planet, exploiting a flaw to counter Frieza",
1719   - "lastPlayedAt": 1788249419549
  1732 + "lastPlayedAt": 1788252971329
1720 1733 },
1721 1734 {
1722 1735 "id": "e6fda5c2237d",
... ... @@ -1761,7 +1774,7 @@
1761 1774 "likes": 0,
1762 1775 "createdAt": 1788005305091,
1763 1776 "promptEn": "facing Frieza, stayed passive and got cornered",
1764   - "lastPlayedAt": 1788246715470
  1777 + "lastPlayedAt": 1788252623220
1765 1778 },
1766 1779 {
1767 1780 "id": "d680e111ade5",
... ... @@ -1776,7 +1789,7 @@
1776 1789 "likes": 0,
1777 1790 "createdAt": 1788005066484,
1778 1791 "promptEn": "Namek is about to be destroyed, the dragon ball wish still up for grabs",
1779   - "lastPlayedAt": 1788246173033
  1792 + "lastPlayedAt": 1788251174432
1780 1793 },
1781 1794 {
1782 1795 "id": "565059763888",
... ... @@ -1791,7 +1804,7 @@
1791 1804 "likes": 0,
1792 1805 "createdAt": 1788004828526,
1793 1806 "promptEn": "in the subway, exploiting a flaw to counter Mahito",
1794   - "lastPlayedAt": 1788249383535
  1807 + "lastPlayedAt": 1788250659744
1795 1808 },
1796 1809 {
1797 1810 "id": "323737948733",
... ... @@ -1806,7 +1819,7 @@
1806 1819 "likes": 0,
1807 1820 "createdAt": 1788004590937,
1808 1821 "promptEn": "facing Mahito, held the goal but paid a price",
1809   - "lastPlayedAt": 1788244441588
  1822 + "lastPlayedAt": 1788253015358
1810 1823 },
1811 1824 {
1812 1825 "id": "9996a8ac8326",
... ... @@ -1821,7 +1834,7 @@
1821 1834 "likes": 0,
1822 1835 "createdAt": 1788004353928,
1823 1836 "promptEn": "facing Mahito, went in head-on and got seen through",
1824   - "lastPlayedAt": 1788250119920
  1837 + "lastPlayedAt": 1788251822858
1825 1838 },
1826 1839 {
1827 1840 "id": "971d80ec087c",
... ... @@ -1836,7 +1849,7 @@
1836 1849 "likes": 0,
1837 1850 "createdAt": 1788004115725,
1838 1851 "promptEn": "facing Mahito, stayed passive and got cornered",
1839   - "lastPlayedAt": 1788249363526
  1852 + "lastPlayedAt": 1788252799252
1840 1853 },
1841 1854 {
1842 1855 "id": "5dd52c568f66",
... ... @@ -1851,7 +1864,7 @@
1851 1864 "likes": 0,
1852 1865 "createdAt": 1788004115612,
1853 1866 "promptEn": "facing Mahito, stayed passive and got cornered",
1854   - "lastPlayedAt": 1788248314826
  1867 + "lastPlayedAt": 1788252553190
1855 1868 },
1856 1869 {
1857 1870 "id": "f4a2870386c6",
... ... @@ -1866,7 +1879,7 @@
1866 1879 "likes": 0,
1867 1880 "createdAt": 1788003877425,
1868 1881 "promptEn": "in the dream, exploiting a flaw to counter Enmu",
1869   - "lastPlayedAt": 1788249187447
  1882 + "lastPlayedAt": 1788252533186
1870 1883 },
1871 1884 {
1872 1885 "id": "7c4f9460e6b7",
... ... @@ -1881,7 +1894,7 @@
1881 1894 "likes": 0,
1882 1895 "createdAt": 1788003639415,
1883 1896 "promptEn": "facing Enmu, held the goal but paid a price",
1884   - "lastPlayedAt": 1788248428932
  1897 + "lastPlayedAt": 1788252106986
1885 1898 },
1886 1899 {
1887 1900 "id": "6881dbe839a0",
... ... @@ -1896,7 +1909,7 @@
1896 1909 "likes": 0,
1897 1910 "createdAt": 1788003402559,
1898 1911 "promptEn": "facing Enmu, went in head-on and got seen through",
1899   - "lastPlayedAt": 1788246589381
  1912 + "lastPlayedAt": 1788252072968
1900 1913 },
1901 1914 {
1902 1915 "id": "5c0f1663f4ae",
... ... @@ -1911,7 +1924,7 @@
1911 1924 "likes": 0,
1912 1925 "createdAt": 1788003166117,
1913 1926 "promptEn": "facing Enmu, stayed passive and got cornered",
1914   - "lastPlayedAt": 1788246683440
  1927 + "lastPlayedAt": 1788252727219
1915 1928 },
1916 1929 {
1917 1930 "id": "a3f39da74c1f",
... ... @@ -1926,7 +1939,7 @@
1926 1939 "likes": 0,
1927 1940 "createdAt": 1788002929455,
1928 1941 "promptEn": "facing Reiner, exploiting a flaw to counter him",
1929   - "lastPlayedAt": 1788243602857
  1942 + "lastPlayedAt": 1788252195030
1930 1943 },
1931 1944 {
1932 1945 "id": "ea8b38014674",
... ... @@ -1941,7 +1954,7 @@
1941 1954 "likes": 0,
1942 1955 "createdAt": 1788002691822,
1943 1956 "promptEn": "facing Reiner, held the goal but paid a price",
1944   - "lastPlayedAt": 1788249915447
  1957 + "lastPlayedAt": 1788252655237
1945 1958 },
1946 1959 {
1947 1960 "id": "f65b15d67966",
... ... @@ -1956,7 +1969,7 @@
1956 1969 "likes": 0,
1957 1970 "createdAt": 1788002455066,
1958 1971 "promptEn": "facing Reiner, went in head-on and got seen through",
1959   - "lastPlayedAt": 1788249057379
  1972 + "lastPlayedAt": 1788251886889
1960 1973 },
1961 1974 {
1962 1975 "id": "5846d645e870",
... ... @@ -1971,7 +1984,7 @@
1971 1984 "likes": 0,
1972 1985 "createdAt": 1788002217928,
1973 1986 "promptEn": "facing Reiner, stayed passive and got cornered",
1974   - "lastPlayedAt": 1788247906463
  1987 + "lastPlayedAt": 1788251696779
1975 1988 },
1976 1989 {
1977 1990 "id": "3fedf5531999",
... ... @@ -1986,7 +1999,7 @@
1986 1999 "likes": 0,
1987 2000 "createdAt": 1788001981272,
1988 2001 "promptEn": "basement in Shiganshina, the titan secret weighing down on you",
1989   - "lastPlayedAt": 1788248186714
  2002 + "lastPlayedAt": 1788252675244
1990 2003 },
1991 2004 {
1992 2005 "id": "7c2351a27a96",
... ... @@ -2001,7 +2014,7 @@
2001 2014 "likes": 0,
2002 2015 "createdAt": 1788001508988,
2003 2016 "promptEn": "in the ruins of Konoha, exploiting a flaw to counter Pain",
2004   - "lastPlayedAt": 1788244357522
  2017 + "lastPlayedAt": 1788252503166
2005 2018 },
2006 2019 {
2007 2020 "id": "79304c061606",
... ... @@ -2016,7 +2029,7 @@
2016 2029 "likes": 0,
2017 2030 "createdAt": 1788001428705,
2018 2031 "promptEn": "in the ruins of Konoha, exploiting a flaw to counter Pain",
2019   - "lastPlayedAt": 1788249429557
  2032 + "lastPlayedAt": 1788251776833
2020 2033 },
2021 2034 {
2022 2035 "id": "e486fab4c24d",
... ... @@ -2031,7 +2044,7 @@
2031 2044 "likes": 0,
2032 2045 "createdAt": 1788001285796,
2033 2046 "promptEn": "in the ruins of Konoha, exploiting a flaw to counter Pain",
2034   - "lastPlayedAt": 1788248869294
  2047 + "lastPlayedAt": 1788252231046
2035 2048 },
2036 2049 {
2037 2050 "id": "808e89ca06a4",
... ... @@ -2046,7 +2059,7 @@
2046 2059 "likes": 0,
2047 2060 "createdAt": 1788001049233,
2048 2061 "promptEn": "facing Pain, held the goal but paid a price",
2049   - "lastPlayedAt": 1788249071386
  2062 + "lastPlayedAt": 1788251780839
2050 2063 },
2051 2064 {
2052 2065 "id": "7ecd480a3c31",
... ... @@ -2061,7 +2074,7 @@
2061 2074 "likes": 0,
2062 2075 "createdAt": 1788000812932,
2063 2076 "promptEn": "facing Pain, went in head-on and got seen through",
2064   - "lastPlayedAt": 1788249183444
  2077 + "lastPlayedAt": 1788252529179
2065 2078 },
2066 2079 {
2067 2080 "id": "f691ac021df0",
... ... @@ -2076,7 +2089,7 @@
2076 2089 "likes": 0,
2077 2090 "createdAt": 1788000577474,
2078 2091 "promptEn": "facing Pain, stayed passive and got cornered",
2079   - "lastPlayedAt": 1788246411240
  2092 + "lastPlayedAt": 1788253009344
2080 2093 },
2081 2094 {
2082 2095 "id": "5ef9489bdc5b",
... ... @@ -2091,7 +2104,7 @@
2091 2104 "likes": 0,
2092 2105 "createdAt": 1788000341927,
2093 2106 "promptEn": "Konoha destroyed by Shinra Tensei, Pain declaring order in the ruins",
2094   - "lastPlayedAt": 1788247898463
  2107 + "lastPlayedAt": 1788251102395
2095 2108 },
2096 2109 {
2097 2110 "id": "e245dbcc6d36",
... ... @@ -2106,7 +2119,7 @@
2106 2119 "likes": 0,
2107 2120 "createdAt": 1788000105889,
2108 2121 "promptEn": "facing Sakazuki, exploiting a flaw to counter him",
2109   - "lastPlayedAt": 1788249083398
  2122 + "lastPlayedAt": 1788250850269
2110 2123 },
2111 2124 {
2112 2125 "id": "c9be7c07b793",
... ... @@ -2121,7 +2134,7 @@
2121 2134 "likes": 0,
2122 2135 "createdAt": 1787999869694,
2123 2136 "promptEn": "facing Sakazuki, held the goal but paid a price",
2124   - "lastPlayedAt": 1788249637645
  2137 + "lastPlayedAt": 1788251688777
2125 2138 },
2126 2139 {
2127 2140 "id": "677941e683c4",
... ... @@ -2136,7 +2149,7 @@
2136 2149 "likes": 0,
2137 2150 "createdAt": 1787999633745,
2138 2151 "promptEn": "facing Sakazuki, went in head-on and got seen through",
2139   - "lastPlayedAt": 1788241280859
  2152 + "lastPlayedAt": 1788251912902
2140 2153 },
2141 2154 {
2142 2155 "id": "932d15f7f8bd",
... ... @@ -2151,7 +2164,7 @@
2151 2164 "likes": 0,
2152 2165 "createdAt": 1787999633670,
2153 2166 "promptEn": "facing Sakazuki, went in head-on and got seen through",
2154   - "lastPlayedAt": 1788248228749
  2167 + "lastPlayedAt": 1788251129144
2155 2168 },
2156 2169 {
2157 2170 "id": "a8b8b4430ee3",
... ... @@ -2166,7 +2179,7 @@
2166 2179 "likes": 0,
2167 2180 "createdAt": 1787999397097,
2168 2181 "promptEn": "facing Sakazuki, stayed passive and got cornered",
2169   - "lastPlayedAt": 1788245170174
  2182 + "lastPlayedAt": 1788252397128
2170 2183 },
2171 2184 {
2172 2185 "id": "88923aaf57f3",
... ... @@ -2181,7 +2194,7 @@
2181 2194 "likes": 0,
2182 2195 "createdAt": 1787999161013,
2183 2196 "promptEn": "execution platform at Marineford, Ace about to be executed, navy vs pirates",
2184   - "lastPlayedAt": 1788242772136
  2197 + "lastPlayedAt": 1788253025350
2185 2198 },
2186 2199 {
2187 2200 "id": "00476b62bf75",
... ... @@ -2196,7 +2209,7 @@
2196 2209 "likes": 0,
2197 2210 "createdAt": 1787903583872,
2198 2211 "promptEn": "constable asks if she's always been alone here, she doesn't answer",
2199   - "lastPlayedAt": 1788249399541
  2212 + "lastPlayedAt": 1788253047365
2200 2213 },
2201 2214 {
2202 2215 "id": "2ab2094e5679",
... ... @@ -2211,7 +2224,7 @@
2211 2224 "likes": 0,
2212 2225 "createdAt": 1787900324972,
2213 2226 "promptEn": "constable declines a drink, asks what this place is, she watches him through the screen",
2214   - "lastPlayedAt": 1788248246767
  2227 + "lastPlayedAt": 1788252709206
2215 2228 },
2216 2229 {
2217 2230 "id": "43ee5c728f1b",
... ... @@ -2226,7 +2239,7 @@
2226 2239 "likes": 0,
2227 2240 "createdAt": 1787850191899,
2228 2241 "promptEn": "constable reaches out to touch her hand through the screen gap, no response",
2229   - "lastPlayedAt": 1788246881603
  2242 + "lastPlayedAt": 1788251708784
2230 2243 },
2231 2244 {
2232 2245 "id": "9bd8c9fbf1f6",
... ... @@ -2241,7 +2254,7 @@
2241 2254 "likes": 0,
2242 2255 "createdAt": 1787847447661,
2243 2256 "promptEn": "constable tells the woman behind the screen he'll trust her tonight",
2244   - "lastPlayedAt": 1788247912475
  2257 + "lastPlayedAt": 1788250278003
2245 2258 },
2246 2259 {
2247 2260 "id": "8cb493338cb3",
... ... @@ -2256,7 +2269,7 @@
2256 2269 "likes": 0,
2257 2270 "createdAt": 1787831362083,
2258 2271 "promptEn": "constable promises to get her out safely if she trusts him",
2259   - "lastPlayedAt": 1788248697169
  2272 + "lastPlayedAt": 1788251652757
2260 2273 },
2261 2274 {
2262 2275 "id": "5ede61c01b22",
... ... @@ -2286,7 +2299,7 @@
2286 2299 "likes": 0,
2287 2300 "createdAt": 1787762332289,
2288 2301 "promptEn": "woman behind the screen stirs and softly reassures him",
2289   - "lastPlayedAt": 1788248214738
  2302 + "lastPlayedAt": 1788251932913
2290 2303 },
2291 2304 {
2292 2305 "id": "0abf6e529c92",
... ... @@ -2301,7 +2314,7 @@
2301 2314 "likes": 0,
2302 2315 "createdAt": 1787762311804,
2303 2316 "promptEn": "constable sits with a cup of water, slowly looking up at the woman behind the screen",
2304   - "lastPlayedAt": 1788250038963
  2317 + "lastPlayedAt": 1788251410573
2305 2318 },
2306 2319 {
2307 2320 "id": "850f13881576",
... ... @@ -2316,7 +2329,7 @@
2316 2329 "likes": 0,
2317 2330 "createdAt": 1787762286261,
2318 2331 "promptEn": "constable stares at her through the screen, silent, saying nothing",
2319   - "lastPlayedAt": 1788248262778
  2332 + "lastPlayedAt": 1788251618745
2320 2333 },
2321 2334 {
2322 2335 "id": "59711a102e84",
... ... @@ -2331,7 +2344,7 @@
2331 2344 "likes": 0,
2332 2345 "createdAt": 1787762224218,
2333 2346 "promptEn": "constable stands under the lamp, holding her gaze through the screen, not backing away",
2334   - "lastPlayedAt": 1788249041367
  2347 + "lastPlayedAt": 1788252377122
2335 2348 },
2336 2349 {
2337 2350 "id": "f2e70205766b",
... ... @@ -2346,7 +2359,7 @@
2346 2359 "likes": 0,
2347 2360 "createdAt": 1787738044140,
2348 2361 "promptEn": "constable carefully checks the door and window latches, she watches from behind the screen",
2349   - "lastPlayedAt": 1788245190190
  2362 + "lastPlayedAt": 1788252857278
2350 2363 },
2351 2364 {
2352 2365 "id": "cfbef6e37ae4",
... ... @@ -2361,7 +2374,7 @@
2361 2374 "likes": 0,
2362 2375 "createdAt": 1787730413689,
2363 2376 "promptEn": "constable grabs the oil lamp and hurls it at the portrait, the scroll catches fire",
2364   - "lastPlayedAt": 1788249473574
  2377 + "lastPlayedAt": 1788251572712
2365 2378 },
2366 2379 {
2367 2380 "id": "e6c23c6adc42",
... ... @@ -2376,7 +2389,7 @@
2376 2389 "likes": 0,
2377 2390 "createdAt": 1787730331754,
2378 2391 "promptEn": "constable steps in from the rainy night, spots her, introduces himself with a cover story",
2379   - "lastPlayedAt": 1788247554188
  2392 + "lastPlayedAt": 1788250400085
2380 2393 },
2381 2394 {
2382 2395 "id": "d720d52c783f",
... ... @@ -2406,7 +2419,7 @@
2406 2419 "likes": 0,
2407 2420 "createdAt": 1787724313620,
2408 2421 "promptEn": "first-person view bursting into the room, glancing back in fear, the woman startled",
2409   - "lastPlayedAt": 1788249869811
  2422 + "lastPlayedAt": 1788252843270
2410 2423 },
2411 2424 {
2412 2425 "id": "81b6c3789111",
... ... @@ -2421,7 +2434,7 @@
2421 2434 "likes": 0,
2422 2435 "createdAt": 1787708713767,
2423 2436 "promptEn": "constable stops looking around, turns to face the screen, speaks to her silhouette",
2424   - "lastPlayedAt": 1788245738674
  2437 + "lastPlayedAt": 1788252811258
2425 2438 },
2426 2439 {
2427 2440 "id": "5dc95a15cd5d",
... ... @@ -2436,7 +2449,7 @@
2436 2449 "likes": 0,
2437 2450 "createdAt": 1787708701547,
2438 2451 "promptEn": "constable gently tells her he'll stay and keep her company",
2439   - "lastPlayedAt": 1788245988880
  2452 + "lastPlayedAt": 1788252873286
2440 2453 },
2441 2454 {
2442 2455 "id": "8d9c1dad041f",
... ... @@ -2466,7 +2479,7 @@
2466 2479 "likes": 0,
2467 2480 "createdAt": 1787708684658,
2468 2481 "promptEn": "constable sees her silent gesture, lets go of his sword hilt, steps closer and agrees",
2469   - "lastPlayedAt": 1788246805543
  2482 + "lastPlayedAt": 1788251970926
2470 2483 },
2471 2484 {
2472 2485 "id": "abdbe25f3121",
... ... @@ -2481,7 +2494,7 @@
2481 2494 "likes": 0,
2482 2495 "createdAt": 1787708674508,
2483 2496 "promptEn": "constable sees through her painted disguise and rips her face off, revealing a demonic visage underneath",
2484   - "lastPlayedAt": 1788248903320
  2497 + "lastPlayedAt": 1788252661239
2485 2498 },
2486 2499 {
2487 2500 "id": "6d433dcc104c",
... ... @@ -2496,7 +2509,7 @@
2496 2509 "likes": 0,
2497 2510 "createdAt": 1787708663418,
2498 2511 "promptEn": "constable catches his breath, thanks her, and rests on the couch while she watches",
2499   - "lastPlayedAt": 1788245906827
  2512 + "lastPlayedAt": 1788250466114
2500 2513 },
2501 2514 {
2502 2515 "id": "cd22c2b491fc",
... ... @@ -2511,7 +2524,7 @@
2511 2524 "likes": 0,
2512 2525 "createdAt": 1787708653181,
2513 2526 "promptEn": "constable holds her gaze, quietly says \"go on\", then waits in silence",
2514   - "lastPlayedAt": 1788249737695
  2527 + "lastPlayedAt": 1788251130405
2515 2528 },
2516 2529 {
2517 2530 "id": "55f98ceec9e4",
... ... @@ -2526,7 +2539,7 @@
2526 2539 "likes": 0,
2527 2540 "createdAt": 1787708648409,
2528 2541 "promptEn": "she reaches a hand through the screen gap beckoning him, he steps back and firmly refuses",
2529   - "lastPlayedAt": 1788248366872
  2542 + "lastPlayedAt": 1788251828861
2530 2543 },
2531 2544 {
2532 2545 "id": "3f0c28f5373a",
... ... @@ -2541,7 +2554,7 @@
2541 2554 "likes": 0,
2542 2555 "createdAt": 1787708646888,
2543 2556 "promptEn": "constable studies her through the screen growing suspicious, hand drifting to his sword",
2544   - "lastPlayedAt": 1788249453571
  2557 + "lastPlayedAt": 1788251602734
2545 2558 },
2546 2559 {
2547 2560 "id": "1c0c203cfb3b",
... ... @@ -2556,7 +2569,7 @@
2556 2569 "likes": 0,
2557 2570 "createdAt": 1787708641782,
2558 2571 "promptEn": "constable sits down at the table, studying her expression through the screen",
2559   - "lastPlayedAt": 1788249369530
  2572 + "lastPlayedAt": 1788252435140
2560 2573 },
2561 2574 {
2562 2575 "id": "89975be9def4",
... ... @@ -2571,7 +2584,7 @@
2571 2584 "likes": 0,
2572 2585 "createdAt": 1787708626228,
2573 2586 "promptEn": "constable drinks some water first, thanks her, watching the screen warily as he drinks",
2574   - "lastPlayedAt": 1788249247478
  2587 + "lastPlayedAt": 1788251160420
2575 2588 },
2576 2589 {
2577 2590 "id": "e08f7f7e54dd",
... ... @@ -2586,7 +2599,7 @@
2586 2599 "likes": 0,
2587 2600 "createdAt": 1787708622306,
2588 2601 "promptEn": "constable gives a clasped-hand salute and thanks her, she gives a subtle nod",
2589   - "lastPlayedAt": 1788242193644
  2602 + "lastPlayedAt": 1788250263997
2590 2603 },
2591 2604 {
2592 2605 "id": "538dc7e5746f",
... ... @@ -2601,7 +2614,7 @@
2601 2614 "likes": 0,
2602 2615 "createdAt": 1787708622178,
2603 2616 "promptEn": "constable gives a clasped-hand salute and thanks her, she gives a subtle nod",
2604   - "lastPlayedAt": 1788247151819
  2617 + "lastPlayedAt": 1788253057370
2605 2618 },
2606 2619 {
2607 2620 "id": "1721c75dbc3b",
... ... @@ -2616,7 +2629,7 @@
2616 2629 "likes": 0,
2617 2630 "createdAt": 1787708615415,
2618 2631 "promptEn": "she slides her hand through the screen gap asking him to warm it, he hesitates without touching",
2619   - "lastPlayedAt": 1788246669438
  2632 + "lastPlayedAt": 1788252911303
2620 2633 },
2621 2634 {
2622 2635 "id": "c2b145b111e2",
... ... @@ -2631,7 +2644,7 @@
2631 2644 "likes": 0,
2632 2645 "createdAt": 1787708613364,
2633 2646 "promptEn": "constable steps in, notices her behind the screen, greets her, she just watches silently",
2634   - "lastPlayedAt": 1788248681145
  2647 + "lastPlayedAt": 1788252945316
2635 2648 },
2636 2649 {
2637 2650 "id": "a5d5d353bbf8",
... ... @@ -2646,7 +2659,7 @@
2646 2659 "likes": 0,
2647 2660 "createdAt": 1787674768144,
2648 2661 "promptEn": "constable lifts a bronze mirror, reflecting candlelight onto the portrait on the wall",
2649   - "lastPlayedAt": 1788249403540
  2662 + "lastPlayedAt": 1788251856874
2650 2663 },
2651 2664 {
2652 2665 "id": "3be1f275fde2",
... ... @@ -2661,7 +2674,7 @@
2661 2674 "likes": 0,
2662 2675 "createdAt": 1787674743188,
2663 2676 "promptEn": "constable draws his sword and breaks the sealed talisman on the screen, revealing her behind it",
2664   - "lastPlayedAt": 1788249643649
  2677 + "lastPlayedAt": 1788252539184
2665 2678 },
2666 2679 {
2667 2680 "id": "693e0b9ca66a",
... ... @@ -2691,7 +2704,7 @@
2691 2704 "likes": 0,
2692 2705 "createdAt": 1787674716075,
2693 2706 "promptEn": "constable watches the oil lamp flame flicker, her silhouette motionless behind the screen",
2694   - "lastPlayedAt": 1788246721475
  2707 + "lastPlayedAt": 1788251352521
2695 2708 },
2696 2709 {
2697 2710 "id": "23d1ae1982e6",
... ... @@ -2706,7 +2719,7 @@
2706 2719 "likes": 0,
2707 2720 "createdAt": 1787674713724,
2708 2721 "promptEn": "constable enters and cautiously looks around, his gaze settling on her silhouette behind the screen",
2709   - "lastPlayedAt": 1788249347518
  2722 + "lastPlayedAt": 1788252056963
2710 2723 },
2711 2724 {
2712 2725 "id": "b69389094416",
... ... @@ -2721,7 +2734,7 @@
2721 2734 "likes": 0,
2722 2735 "createdAt": 1787674695703,
2723 2736 "promptEn": "constable stops, faces the screen, quietly promises to keep his voice down",
2724   - "lastPlayedAt": 1788250023879
  2737 + "lastPlayedAt": 1788252743222
2725 2738 },
2726 2739 {
2727 2740 "id": "94dc455e26bf",
... ... @@ -2736,7 +2749,7 @@
2736 2749 "likes": 0,
2737 2750 "createdAt": 1787674688717,
2738 2751 "promptEn": "constable agrees to stay, lets his guard down, she gives a barely-there nod",
2739   - "lastPlayedAt": 1788248320834
  2752 + "lastPlayedAt": 1788250832261
2740 2753 },
2741 2754 {
2742 2755 "id": "4874fe3fdbc9",
... ... @@ -2781,7 +2794,7 @@
2781 2794 "likes": 0,
2782 2795 "createdAt": 1787674642026,
2783 2796 "promptEn": "constable enters from the side and sits down on a stool by the lamp to rest",
2784   - "lastPlayedAt": 1788247574201
  2797 + "lastPlayedAt": 1788251330505
2785 2798 },
2786 2799 {
2787 2800 "id": "fe7ffbe65653",
... ... @@ -2796,7 +2809,7 @@
2796 2809 "likes": 0,
2797 2810 "createdAt": 1787674639055,
2798 2811 "promptEn": "constable checks his reflection in the mirror, sees her behind his shoulder in it, turns around to find the screen empty",
2799   - "lastPlayedAt": 1788249777714
  2812 + "lastPlayedAt": 1788253073377
2800 2813 },
2801 2814 {
2802 2815 "id": "ebaf0dde3d9f",
... ... @@ -2811,7 +2824,7 @@
2811 2824 "likes": 0,
2812 2825 "createdAt": 1787674623678,
2813 2826 "promptEn": "constable bows to the screen, thanking her for sheltering him",
2814   - "lastPlayedAt": 1788248150683
  2827 + "lastPlayedAt": 1788251144418
2815 2828 },
2816 2829 {
2817 2830 "id": "d94991a1e21b",
... ... @@ -2841,7 +2854,7 @@
2841 2854 "likes": 0,
2842 2855 "createdAt": 1787674446841,
2843 2856 "promptEn": "player panics into the room, glances back, out of breath, says something's chasing him outside",
2844   - "lastPlayedAt": 1788247932497
  2857 + "lastPlayedAt": 1788250856275
2845 2858 },
2846 2859 {
2847 2860 "id": "c25d53a5e086",
... ... @@ -2856,7 +2869,7 @@
2856 2869 "likes": 0,
2857 2870 "createdAt": 1787674395006,
2858 2871 "promptEn": "constable and the woman hold a tense silent stare through the screen",
2859   - "lastPlayedAt": 1788248761225
  2872 + "lastPlayedAt": 1788252897296
2860 2873 },
2861 2874 {
2862 2875 "id": "f264bce22903",
... ... @@ -2871,7 +2884,7 @@
2871 2884 "likes": 0,
2872 2885 "createdAt": 1787674361187,
2873 2886 "promptEn": "constable presses his hand to his chest, vowing his heart to her, she gives a knowing smile",
2874   - "lastPlayedAt": 1788247321970
  2887 + "lastPlayedAt": 1788252451148
2875 2888 },
2876 2889 {
2877 2890 "id": "8aba0a6bf05b",
... ... @@ -2886,7 +2899,7 @@
2886 2899 "likes": 0,
2887 2900 "createdAt": 1787674351536,
2888 2901 "promptEn": "constable searches an old house at night, candle flickering, woman hiding behind the screen",
2889   - "lastPlayedAt": 1788246491297
  2902 + "lastPlayedAt": 1788250201967
2890 2903 },
2891 2904 {
2892 2905 "id": "dc13e4f165dc",
... ... @@ -2901,7 +2914,7 @@
2901 2914 "likes": 0,
2902 2915 "createdAt": 1787672849330,
2903 2916 "promptEn": "constable cautiously follows her silent gesture, hand near his sword, tracking her movement",
2904   - "lastPlayedAt": 1788245314287
  2917 + "lastPlayedAt": 1788252175018
2905 2918 },
2906 2919 {
2907 2920 "id": "feb4df69e1a2",
... ... @@ -2916,7 +2929,7 @@
2916 2929 "likes": 0,
2917 2930 "createdAt": 1787665100787,
2918 2931 "promptEn": "constable politely asks the woman behind the screen for tea",
2919   - "lastPlayedAt": 1788246062936
  2932 + "lastPlayedAt": 1788250618187
2920 2933 },
2921 2934 {
2922 2935 "id": "d2c77b87b84b",
... ... @@ -2930,7 +2943,7 @@
2930 2943 "partition": "ref2va",
2931 2944 "likes": 0,
2932 2945 "createdAt": 1787652321725,
2933   - "lastPlayedAt": 1788246485298,
  2946 + "lastPlayedAt": 1788250646200,
2934 2947 "promptEn": "constable talking to the woman through the screen"
2935 2948 },
2936 2949 {
... ... @@ -2961,7 +2974,7 @@
2961 2974 "likes": 0,
2962 2975 "createdAt": 1787640567001,
2963 2976 "promptEn": "summoned toad Wentai, beat the painted-skin female ghost",
2964   - "lastPlayedAt": 1788249437562
  2977 + "lastPlayedAt": 1788252685196
2965 2978 },
2966 2979 {
2967 2980 "id": "1cc42a8669a1",
... ... @@ -2975,7 +2988,7 @@
2975 2988 "partition": "ref2va",
2976 2989 "likes": 0,
2977 2990 "createdAt": 1787639957338,
2978   - "lastPlayedAt": 1788248897311,
  2991 + "lastPlayedAt": 1788251344514,
2979 2992 "promptEn": "constable talking to the woman through the screen"
2980 2993 },
2981 2994 {
... ... @@ -2991,7 +3004,7 @@
2991 3004 "likes": 0,
2992 3005 "createdAt": 1787637292741,
2993 3006 "promptEn": "constable bursts in panicked, spots the woman behind the screen",
2994   - "lastPlayedAt": 1788248346857
  3007 + "lastPlayedAt": 1788251872881
2995 3008 },
2996 3009 {
2997 3010 "id": "93c2b84a2a10",
... ... @@ -3006,7 +3019,7 @@
3006 3019 "likes": 0,
3007 3020 "createdAt": 1787635935502,
3008 3021 "promptEn": "constable bursts in panicked, spots the woman behind the screen",
3009   - "lastPlayedAt": 1788246137001
  3022 + "lastPlayedAt": 1788250520137
3010 3023 },
3011 3024 {
3012 3025 "id": "b32769bf80db",
... ... @@ -3021,7 +3034,7 @@
3021 3034 "likes": 0,
3022 3035 "createdAt": 1787632587351,
3023 3036 "promptEn": "constable bursts in panicked, spots the woman behind the screen",
3024   - "lastPlayedAt": 1788248791245
  3037 + "lastPlayedAt": 1788250612184
3025 3038 },
3026 3039 {
3027 3040 "id": "282281777d1b",
... ... @@ -3036,7 +3049,7 @@
3036 3049 "likes": 0,
3037 3050 "createdAt": 1787615243753,
3038 3051 "promptEn": "robot arm picks up a red block and puts it down",
3039   - "lastPlayedAt": 1788249857808
  3052 + "lastPlayedAt": 1788251258468
3040 3053 },
3041 3054 {
3042 3055 "id": "d040eec16a88",
... ... @@ -3051,7 +3064,7 @@
3051 3064 "likes": 0,
3052 3065 "createdAt": 1787615242602,
3053 3066 "promptEn": "otome game male lead showcase PV",
3054   - "lastPlayedAt": 1788249753702
  3067 + "lastPlayedAt": 1788251108401
3055 3068 },
3056 3069 {
3057 3070 "id": "423a20afba5a",
... ... @@ -3066,7 +3079,7 @@
3066 3079 "likes": 0,
3067 3080 "createdAt": 1787615241814,
3068 3081 "promptEn": "xianxia character turning, only back and side profile shown",
3069   - "lastPlayedAt": 1788247390027
  3082 + "lastPlayedAt": 1788250526137
3070 3083 },
3071 3084 {
3072 3085 "id": "1a398591663e",
... ... @@ -3081,7 +3094,7 @@
3081 3094 "likes": 0,
3082 3095 "createdAt": 1787615240988,
3083 3096 "promptEn": "clay fox sprints off a cliff, slow-mo leap over the canyon",
3084   - "lastPlayedAt": 1788249951846
  3097 + "lastPlayedAt": 1788251894893
3085 3098 },
3086 3099 {
3087 3100 "id": "db125dc6a831",
... ... @@ -3095,7 +3108,7 @@
3095 3108 "partition": "text",
3096 3109 "likes": 0,
3097 3110 "createdAt": 1787615240199,
3098   - "lastPlayedAt": 1788244934012,
  3111 + "lastPlayedAt": 1788251786843,
3099 3112 "promptEn": "website UI title slides in, car light fades to red"
3100 3113 },
3101 3114 {
... ... @@ -3111,7 +3124,7 @@
3111 3124 "likes": 0,
3112 3125 "createdAt": 1787588547172,
3113 3126 "promptEn": "product website scrolling down with flashy effects",
3114   - "lastPlayedAt": 1788248841279
  3127 + "lastPlayedAt": 1788251228453
3115 3128 },
3116 3129 {
3117 3130 "id": "2e18bf09b46a",
... ... @@ -3141,7 +3154,7 @@
3141 3154 "likes": 0,
3142 3155 "createdAt": 1787588545243,
3143 3156 "promptEn": "ergonomic office chair 360 showcase ad",
3144   - "lastPlayedAt": 1788250058966
  3157 + "lastPlayedAt": 1788253031360
3145 3158 },
3146 3159 {
3147 3160 "id": "b0d258352c3c",
... ... @@ -3171,7 +3184,7 @@
3171 3184 "likes": 0,
3172 3185 "createdAt": 1787588544109,
3173 3186 "promptEn": "vampire romance drama trailer, girl wanders into the castle",
3174   - "lastPlayedAt": 1788248955338
  3187 + "lastPlayedAt": 1788250187972
3175 3188 },
3176 3189 {
3177 3190 "id": "7ded7db676d3",
... ... @@ -3186,7 +3199,7 @@
3186 3199 "likes": 0,
3187 3200 "createdAt": 1787588543551,
3188 3201 "promptEn": "MiniMax H3 tech brand film, blue waveform logo",
3189   - "lastPlayedAt": 1788246819557
  3202 + "lastPlayedAt": 1788250954321
3190 3203 },
3191 3204 {
3192 3205 "id": "81711242178d",
... ... @@ -3201,7 +3214,7 @@
3201 3214 "likes": 0,
3202 3215 "createdAt": 1787588542968,
3203 3216 "promptEn": "mystery crime title sequence, collage split-screen credits",
3204   - "lastPlayedAt": 1788247850421
  3217 + "lastPlayedAt": 1788251444607
3205 3218 },
3206 3219 {
3207 3220 "id": "67ababbea78f",
... ... @@ -3216,7 +3229,7 @@
3216 3229 "likes": 0,
3217 3230 "createdAt": 1787588542426,
3218 3231 "promptEn": "fashion guy in sunglasses and coat by a fire",
3219   - "lastPlayedAt": 1788245774711
  3232 + "lastPlayedAt": 1788250364064
3220 3233 },
3221 3234 {
3222 3235 "id": "c1237b599bf4",
... ... @@ -3231,7 +3244,7 @@
3231 3244 "likes": 0,
3232 3245 "createdAt": 1787588541726,
3233 3246 "promptEn": "woman opens the trunk on a desert road, takes out a bag",
3234   - "lastPlayedAt": 1788250107920
  3247 + "lastPlayedAt": 1788251984934
3235 3248 },
3236 3249 {
3237 3250 "id": "97846542ab09",
... ... @@ -3246,7 +3259,7 @@
3246 3259 "likes": 0,
3247 3260 "createdAt": 1787588541164,
3248 3261 "promptEn": "tiny silhouette before a huge cosmic gate, title fades in",
3249   - "lastPlayedAt": 1788248234756
  3262 + "lastPlayedAt": 1788252110986
3250 3263 },
3251 3264 {
3252 3265 "id": "5cc09dcdf470",
... ... @@ -3261,7 +3274,7 @@
3261 3274 "likes": 0,
3262 3275 "createdAt": 1787588540642,
3263 3276 "promptEn": "telescope view searching for a MiniMax device, fashionable passerby",
3264   - "lastPlayedAt": 1788248919321
  3277 + "lastPlayedAt": 1788250804249
3265 3278 },
3266 3279 {
3267 3280 "id": "a59cae01ec39",
... ... @@ -3291,7 +3304,7 @@
3291 3304 "likes": 0,
3292 3305 "createdAt": 1787588114381,
3293 3306 "promptEn": "underground rap duo freestyling at the camera",
3294   - "lastPlayedAt": 1788248935327
  3307 + "lastPlayedAt": 1788251744817
3295 3308 },
3296 3309 {
3297 3310 "id": "58a439a93c02",
... ... @@ -3306,7 +3319,7 @@
3306 3319 "likes": 0,
3307 3320 "createdAt": 1787577878370,
3308 3321 "promptEn": "man barges into the old house apologizing, woman in white behind the screen",
3309   - "lastPlayedAt": 1788245984875
  3322 + "lastPlayedAt": 1788250294012
3310 3323 },
3311 3324 {
3312 3325 "id": "0e75ef71c3d0",
... ... @@ -3321,7 +3334,7 @@
3321 3334 "likes": 0,
3322 3335 "createdAt": 1787577088842,
3323 3336 "promptEn": "man thanks the woman for taking him in, behind the screen",
3324   - "lastPlayedAt": 1788247530161
  3337 + "lastPlayedAt": 1788252705203
3325 3338 },
3326 3339 {
3327 3340 "id": "e656777b6eef",
... ... @@ -3336,7 +3349,7 @@
3336 3349 "likes": 0,
3337 3350 "createdAt": 1787577088778,
3338 3351 "promptEn": "man thanks the woman for taking him in",
3339   - "lastPlayedAt": 1788246381212
  3352 + "lastPlayedAt": 1788252149006
3340 3353 },
3341 3354 {
3342 3355 "id": "8d0270466c35",
... ... @@ -3351,7 +3364,7 @@
3351 3364 "likes": 0,
3352 3365 "createdAt": 1787576943673,
3353 3366 "promptEn": "man lowers his voice like she said, checks the door, window and lamp",
3354   - "lastPlayedAt": 1788249077393
  3367 + "lastPlayedAt": 1788251202953
3355 3368 },
3356 3369 {
3357 3370 "id": "396de15c1439",
... ... @@ -3381,7 +3394,7 @@
3381 3394 "likes": 0,
3382 3395 "createdAt": 1787576798050,
3383 3396 "promptEn": "man says he's willing to give her his heart",
3384   - "lastPlayedAt": 1788245790726
  3397 + "lastPlayedAt": 1788252199033
3385 3398 },
3386 3399 {
3387 3400 "id": "57f2c6fa351d",
... ... @@ -3396,7 +3409,7 @@
3396 3409 "likes": 0,
3397 3410 "createdAt": 1787576651668,
3398 3411 "promptEn": "man silently looks at her, doesn't respond",
3399   - "lastPlayedAt": 1788248969344
  3412 + "lastPlayedAt": 1788251536684
3400 3413 },
3401 3414 {
3402 3415 "id": "5958b86ab8f4",
... ... @@ -3486,7 +3499,7 @@
3486 3499 "likes": 0,
3487 3500 "createdAt": 1787575164766,
3488 3501 "promptEn": "man apologizes, says something outside was chasing him",
3489   - "lastPlayedAt": 1788248174703
  3502 + "lastPlayedAt": 1788251998941
3490 3503 },
3491 3504 {
3492 3505 "id": "fb3000701384",
... ... @@ -3501,7 +3514,7 @@
3501 3514 "likes": 0,
3502 3515 "createdAt": 1787573698485,
3503 3516 "promptEn": "underground rap duo freestyling at the camera",
3504   - "lastPlayedAt": 1788248190717
  3517 + "lastPlayedAt": 1788252777241
3505 3518 },
3506 3519 {
3507 3520 "id": "e5246eae814e",
... ... @@ -3516,7 +3529,7 @@
3516 3529 "likes": 0,
3517 3530 "createdAt": 1787569314178,
3518 3531 "promptEn": "man apologizes, says something outside was chasing him",
3519   - "lastPlayedAt": 1788247634250
  3532 + "lastPlayedAt": 1788252793246
3520 3533 },
3521 3534 {
3522 3535 "id": "a0d7a106b8d6",
... ... @@ -3530,7 +3543,7 @@
3530 3543 "partition": "text",
3531 3544 "likes": 0,
3532 3545 "createdAt": 1787568512602,
3533   - "lastPlayedAt": 1788246313154,
  3546 + "lastPlayedAt": 1788252082974,
3534 3547 "promptEn": "man apologizes, says something outside was chasing him"
3535 3548 },
3536 3549 {
... ... @@ -3545,7 +3558,7 @@
3545 3558 "partition": "ref2va",
3546 3559 "likes": 0,
3547 3560 "createdAt": 1787559156410,
3548   - "lastPlayedAt": 1788248745205,
  3561 + "lastPlayedAt": 1788251306490,
3549 3562 "promptEn": "two characters sitting, game menu UI swaps in a mechanical arm"
3550 3563 },
3551 3564 {
... ... @@ -3561,7 +3574,7 @@
3561 3574 "likes": 0,
3562 3575 "createdAt": 1787542770522,
3563 3576 "promptEn": "clip cut to match a reference video's editing rhythm",
3564   - "lastPlayedAt": 1788249969849
  3577 + "lastPlayedAt": 1788251906899
3565 3578 },
3566 3579 {
3567 3580 "id": "f9286d694bad",
... ... @@ -3576,7 +3589,7 @@
3576 3589 "likes": 0,
3577 3590 "createdAt": 1787497396594,
3578 3591 "promptEn": "otome game male lead showcase PV",
3579   - "lastPlayedAt": 1788249489586
  3592 + "lastPlayedAt": 1788250885690
3580 3593 },
3581 3594 {
3582 3595 "id": "36ac12f2f29e",
... ... @@ -3591,7 +3604,7 @@
3591 3604 "likes": 0,
3592 3605 "createdAt": 1787493339415,
3593 3606 "promptEn": "game menu UI, character swaps out a mechanical arm",
3594   - "lastPlayedAt": 1788247514144
  3607 + "lastPlayedAt": 1788252205054
3595 3608 },
3596 3609 {
3597 3610 "id": "656c897b04fc",
... ... @@ -3606,7 +3619,7 @@
3606 3619 "likes": 0,
3607 3620 "createdAt": 1787493303602,
3608 3621 "promptEn": "game menu UI, character swaps out a mechanical arm",
3609   - "lastPlayedAt": 1788246195051
  3622 + "lastPlayedAt": 1788252523177
3610 3623 },
3611 3624 {
3612 3625 "id": "e1fd99e4ab97",
... ... @@ -3621,7 +3634,7 @@
3621 3634 "likes": 0,
3622 3635 "createdAt": 1787470101960,
3623 3636 "promptEn": "cute hand-drawn beagle dog making faces, game screen",
3624   - "lastPlayedAt": 1788249793727
  3637 + "lastPlayedAt": 1788252643229
3625 3638 },
3626 3639 {
3627 3640 "id": "4067993ff824",
... ... @@ -3636,7 +3649,7 @@
3636 3649 "likes": 0,
3637 3650 "createdAt": 1787470101693,
3638 3651 "promptEn": "black-background product-launch style shot of a roast duck",
3639   - "lastPlayedAt": 1788243072394
  3652 + "lastPlayedAt": 1788251246462
3640 3653 },
3641 3654 {
3642 3655 "id": "99e771bbb50d",
... ... @@ -3651,7 +3664,7 @@
3651 3664 "likes": 0,
3652 3665 "createdAt": 1787470101411,
3653 3666 "promptEn": "vertical family-fight drama, arguing with an older woman",
3654   - "lastPlayedAt": 1788247732323
  3667 + "lastPlayedAt": 1788251276478
3655 3668 },
3656 3669 {
3657 3670 "id": "57169e6751d0",
... ... @@ -3666,7 +3679,7 @@
3666 3679 "likes": 0,
3667 3680 "createdAt": 1787470101060,
3668 3681 "promptEn": "snowy bamboo forest at night, wuxia mystery standoff",
3669   - "lastPlayedAt": 1788248617097
  3682 + "lastPlayedAt": 1788250430095
3670 3683 },
3671 3684 {
3672 3685 "id": "125751cac1c8",
... ... @@ -3681,7 +3694,7 @@
3681 3694 "likes": 0,
3682 3695 "createdAt": 1787470100377,
3683 3696 "promptEn": "all-black background, experimental Chinese kinetic typography",
3684   - "lastPlayedAt": 1788247193855
  3697 + "lastPlayedAt": 1788250298018
3685 3698 },
3686 3699 {
3687 3700 "id": "c24c15736daa",
... ... @@ -3711,7 +3724,7 @@
3711 3724 "likes": 0,
3712 3725 "createdAt": 1787470100022,
3713 3726 "promptEn": "MiniMax H3 brand motion graphics, a dot growing into a network",
3714   - "lastPlayedAt": 1788248873291
  3727 + "lastPlayedAt": 1788251916904
3715 3728 },
3716 3729 {
3717 3730 "id": "eaf2a1433c41",
... ... @@ -3726,7 +3739,7 @@
3726 3739 "likes": 0,
3727 3740 "createdAt": 1787470099635,
3728 3741 "promptEn": "pointing at things at a beach market, they turn into hand-drawn animation",
3729   - "lastPlayedAt": 1788249097406
  3742 + "lastPlayedAt": 1788251368535
3730 3743 },
3731 3744 {
3732 3745 "id": "0fe0f2101c44",
... ... @@ -3741,7 +3754,7 @@
3741 3754 "likes": 0,
3742 3755 "createdAt": 1787470099306,
3743 3756 "promptEn": "late-night laundromat footage mixed with glowing hand-drawn animation",
3744   - "lastPlayedAt": 1788249293496
  3757 + "lastPlayedAt": 1788251502656
3745 3758 },
3746 3759 {
3747 3760 "id": "c0e23beb4132",
... ... @@ -3756,7 +3769,7 @@
3756 3769 "likes": 0,
3757 3770 "createdAt": 1787470098914,
3758 3771 "promptEn": "evening kitchen footage mixed with a glowing hand-drawn creature",
3759   - "lastPlayedAt": 1788249331511
  3772 + "lastPlayedAt": 1788252599204
3760 3773 },
3761 3774 {
3762 3775 "id": "6639b07acc7b",
... ... @@ -3771,7 +3784,7 @@
3771 3784 "likes": 0,
3772 3785 "createdAt": 1787470098570,
3773 3786 "promptEn": "fast-cut spaceship trailer title sequence",
3774   - "lastPlayedAt": 1788247744340
  3787 + "lastPlayedAt": 1788252235049
3775 3788 },
3776 3789 {
3777 3790 "id": "1e55e540df11",
... ... @@ -3786,7 +3799,7 @@
3786 3799 "likes": 0,
3787 3800 "createdAt": 1787461994394,
3788 3801 "promptEn": "a quiet mountain village at dawn (second attempt)",
3789   - "lastPlayedAt": 1788248859286
  3802 + "lastPlayedAt": 1788251098955
3790 3803 },
3791 3804 {
3792 3805 "id": "8e16b6faafd9",
... ... @@ -3801,7 +3814,7 @@
3801 3814 "likes": 0,
3802 3815 "createdAt": 1787455895150,
3803 3816 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3804   - "lastPlayedAt": 1788249507592
  3817 + "lastPlayedAt": 1788252253059
3805 3818 },
3806 3819 {
3807 3820 "id": "2a4e45820135",
... ... @@ -3816,7 +3829,7 @@
3816 3829 "likes": 0,
3817 3830 "createdAt": 1787453697093,
3818 3831 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3819   - "lastPlayedAt": 1788248464962
  3832 + "lastPlayedAt": 1788250986335
3820 3833 },
3821 3834 {
3822 3835 "id": "bfe13d1cc4a2",
... ... @@ -3861,7 +3874,7 @@
3861 3874 "likes": 0,
3862 3875 "createdAt": 1787447175021,
3863 3876 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3864   - "lastPlayedAt": 1788247824398
  3877 + "lastPlayedAt": 1788252583201
3865 3878 },
3866 3879 {
3867 3880 "id": "4580ca66725a",
... ... @@ -3876,7 +3889,7 @@
3876 3889 "likes": 0,
3877 3890 "createdAt": 1787444313690,
3878 3891 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3879   - "lastPlayedAt": 1788246104977
  3892 + "lastPlayedAt": 1788250484120
3880 3893 },
3881 3894 {
3882 3895 "id": "e56962bd770f",
... ... @@ -3906,7 +3919,7 @@
3906 3919 "likes": 0,
3907 3920 "createdAt": 1787437988124,
3908 3921 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3909   - "lastPlayedAt": 1788247948508
  3922 + "lastPlayedAt": 1788252010944
3910 3923 },
3911 3924 {
3912 3925 "id": "e497093c79ce",
... ... @@ -3951,7 +3964,7 @@
3951 3964 "likes": 0,
3952 3965 "createdAt": 1787428860940,
3953 3966 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3954   - "lastPlayedAt": 1788249125420
  3967 + "lastPlayedAt": 1788252981335
3955 3968 },
3956 3969 {
3957 3970 "id": "dbc7c27b72ba",
... ... @@ -3981,7 +3994,7 @@
3981 3994 "likes": 0,
3982 3995 "createdAt": 1787426484939,
3983 3996 "promptEn": "small boat sailing through a misty forest river",
3984   - "lastPlayedAt": 1788248206733
  3997 + "lastPlayedAt": 1788252927307
3985 3998 },
3986 3999 {
3987 4000 "id": "9fbcf0d87021",
... ... @@ -4011,7 +4024,7 @@
4011 4024 "likes": 0,
4012 4025 "createdAt": 1787415067615,
4013 4026 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4014   - "lastPlayedAt": 1788249017364
  4027 + "lastPlayedAt": 1788250586170
4015 4028 },
4016 4029 {
4017 4030 "id": "9bcff387cddf",
... ... @@ -4026,7 +4039,7 @@
4026 4039 "likes": 0,
4027 4040 "createdAt": 1787413999418,
4028 4041 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4029   - "lastPlayedAt": 1788247492126
  4042 + "lastPlayedAt": 1788251954922
4030 4043 },
4031 4044 {
4032 4045 "id": "ade0318a7a4a",
... ... @@ -4041,7 +4054,7 @@
4041 4054 "likes": 0,
4042 4055 "createdAt": 1787413511959,
4043 4056 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4044   - "lastPlayedAt": 1788241595119
  4057 + "lastPlayedAt": 1788251161159
4045 4058 },
4046 4059 {
4047 4060 "id": "5d4f40bf0889",
... ... @@ -4056,7 +4069,7 @@
4056 4069 "likes": 0,
4057 4070 "createdAt": 1787412952086,
4058 4071 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4059   - "lastPlayedAt": 1788245274259
  4072 + "lastPlayedAt": 1788251143150
4060 4073 },
4061 4074 {
4062 4075 "id": "59fd7a087f5b",
... ... @@ -4071,7 +4084,7 @@
4071 4084 "likes": 0,
4072 4085 "createdAt": 1787412606465,
4073 4086 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4074   - "lastPlayedAt": 1788249769708
  4087 + "lastPlayedAt": 1788251848870
4075 4088 },
4076 4089 {
4077 4090 "id": "29a99dc412b2",
... ... @@ -4086,7 +4099,7 @@
4086 4099 "likes": 0,
4087 4100 "createdAt": 1787412150062,
4088 4101 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4089   - "lastPlayedAt": 1788249081394
  4102 + "lastPlayedAt": 1788252809259
4090 4103 },
4091 4104 {
4092 4105 "id": "9d2486d6f969",
... ... @@ -4101,7 +4114,7 @@
4101 4114 "likes": 0,
4102 4115 "createdAt": 1787397450973,
4103 4116 "promptEn": "a cat sitting on a windowsill",
4104   - "lastPlayedAt": 1788249343516
  4117 + "lastPlayedAt": 1788251736809
4105 4118 },
4106 4119 {
4107 4120 "id": "580d1d722adc",
... ... @@ -4116,7 +4129,7 @@
4116 4129 "likes": 0,
4117 4130 "createdAt": 1787388090646,
4118 4131 "promptEn": "a cat sitting on a windowsill",
4119   - "lastPlayedAt": 1788245762695
  4132 + "lastPlayedAt": 1788252393130
4120 4133 },
4121 4134 {
4122 4135 "id": "c551457bc55e",
... ... @@ -4130,7 +4143,7 @@
4130 4143 "partition": "text",
4131 4144 "likes": 0,
4132 4145 "createdAt": 1787386316175,
4133   - "lastPlayedAt": 1788245308283,
  4146 + "lastPlayedAt": 1788253015347,
4134 4147 "promptEn": "a cat sitting on a windowsill"
4135 4148 },
4136 4149 {
... ... @@ -4146,7 +4159,7 @@
4146 4159 "likes": 0,
4147 4160 "createdAt": 1787384983124,
4148 4161 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4149   - "lastPlayedAt": 1788247864432
  4162 + "lastPlayedAt": 1788251892892
4150 4163 },
4151 4164 {
4152 4165 "id": "7753a046ba79",
... ... @@ -4191,7 +4204,7 @@
4191 4204 "likes": 0,
4192 4205 "createdAt": 1787375413935,
4193 4206 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4194   - "lastPlayedAt": 1788248671135
  4207 + "lastPlayedAt": 1788252421136
4195 4208 },
4196 4209 {
4197 4210 "id": "10112fb3c1fd",
... ... @@ -4206,7 +4219,7 @@
4206 4219 "likes": 0,
4207 4220 "createdAt": 1787375413623,
4208 4221 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4209   - "lastPlayedAt": 1788248286801
  4222 + "lastPlayedAt": 1788250362064
4210 4223 },
4211 4224 {
4212 4225 "id": "78e698c924b1",
... ... @@ -4221,7 +4234,7 @@
4221 4234 "likes": 0,
4222 4235 "createdAt": 1787375413203,
4223 4236 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4224   - "lastPlayedAt": 1788247438073
  4237 + "lastPlayedAt": 1788252725215
4225 4238 },
4226 4239 {
4227 4240 "id": "219ed88c3ea7",
... ... @@ -4236,7 +4249,7 @@
4236 4249 "likes": 0,
4237 4250 "createdAt": 1787375412946,
4238 4251 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4239   - "lastPlayedAt": 1788245734672
  4252 + "lastPlayedAt": 1788252407133
4240 4253 },
4241 4254 {
4242 4255 "id": "a72236902363",
... ... @@ -4251,7 +4264,7 @@
4251 4264 "likes": 0,
4252 4265 "createdAt": 1787367454249,
4253 4266 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4254   - "lastPlayedAt": 1788246703457
  4267 + "lastPlayedAt": 1788252831263
4255 4268 },
4256 4269 {
4257 4270 "id": "c48af2f34e62",
... ... @@ -4266,7 +4279,7 @@
4266 4279 "likes": 0,
4267 4280 "createdAt": 1787367454065,
4268 4281 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4269   - "lastPlayedAt": 1788245668612
  4282 + "lastPlayedAt": 1788250848268
4270 4283 },
4271 4284 {
4272 4285 "id": "e6c241be377f",
... ... @@ -4281,7 +4294,7 @@
4281 4294 "likes": 0,
4282 4295 "createdAt": 1787367453839,
4283 4296 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4284   - "lastPlayedAt": 1788244952031
  4297 + "lastPlayedAt": 1788252006941
4285 4298 },
4286 4299 {
4287 4300 "id": "8402adae59dc",
... ... @@ -4296,7 +4309,7 @@
4296 4309 "likes": 0,
4297 4310 "createdAt": 1787363956909,
4298 4311 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4299   - "lastPlayedAt": 1788247205863
  4312 + "lastPlayedAt": 1788250706222
4300 4313 },
4301 4314 {
4302 4315 "id": "5692965402f5",
... ... @@ -4311,7 +4324,7 @@
4311 4324 "likes": 0,
4312 4325 "createdAt": 1787332996051,
4313 4326 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4314   - "lastPlayedAt": 1788247490125
  4327 + "lastPlayedAt": 1788250762247
4315 4328 },
4316 4329 {
4317 4330 "id": "bf7d973d02a3",
... ... @@ -4326,7 +4339,7 @@
4326 4339 "likes": 0,
4327 4340 "createdAt": 1787332047061,
4328 4341 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4329   - "lastPlayedAt": 1788241785296
  4342 + "lastPlayedAt": 1788251350521
4330 4343 },
4331 4344 {
4332 4345 "id": "a5688d8f5db3",
... ... @@ -4341,7 +4354,7 @@
4341 4354 "likes": 0,
4342 4355 "createdAt": 1787330101793,
4343 4356 "promptEn": "a red apple on a wooden table, cinematic lighting",
4344   - "lastPlayedAt": 1788246901624
  4357 + "lastPlayedAt": 1788252205039
4345 4358 },
4346 4359 {
4347 4360 "id": "0024d3c2d4cb",
... ... @@ -4356,7 +4369,7 @@
4356 4369 "likes": 0,
4357 4370 "createdAt": 1787326019663,
4358 4371 "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella",
4359   - "lastPlayedAt": 1788248282796
  4372 + "lastPlayedAt": 1788252891295
4360 4373 },
4361 4374 {
4362 4375 "id": "ab42a6fba515",
... ... @@ -4386,7 +4399,7 @@
4386 4399 "likes": 0,
4387 4400 "createdAt": 1787309864037,
4388 4401 "promptEn": "transparent gaming mouse product film, blue and orange lighting",
4389   - "lastPlayedAt": 1788248827266
  4402 + "lastPlayedAt": 1788252567195
4390 4403 },
4391 4404 {
4392 4405 "id": "073f5df6427a",
... ... @@ -4401,7 +4414,7 @@
4401 4414 "likes": 0,
4402 4415 "createdAt": 1787305544815,
4403 4416 "promptEn": "transparent gaming mouse product film, blue and orange lighting",
4404   - "lastPlayedAt": 1788247706310
  4417 + "lastPlayedAt": 1788251648757
4405 4418 },
4406 4419 {
4407 4420 "id": "e23b04ac2c78",
... ... @@ -4416,7 +4429,7 @@
4416 4429 "likes": 0,
4417 4430 "createdAt": 1787304875681,
4418 4431 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4419   - "lastPlayedAt": 1788249393534
  4432 + "lastPlayedAt": 1788253051367
4420 4433 },
4421 4434 {
4422 4435 "id": "c84bc1c7bfa0",
... ... @@ -4431,7 +4444,7 @@
4431 4444 "likes": 0,
4432 4445 "createdAt": 1787304031214,
4433 4446 "promptEn": "constable asks how she's sure it's not human, she says a person would make a sound",
4434   - "lastPlayedAt": 1788249309501
  4447 + "lastPlayedAt": 1788251466626
4435 4448 },
4436 4449 {
4437 4450 "id": "23d2fd81eb4d",
... ... @@ -4446,7 +4459,7 @@
4446 4459 "likes": 0,
4447 4460 "createdAt": 1787304031103,
4448 4461 "promptEn": "constable asks how she's sure it's not human, she says a person would make a sound",
4449   - "lastPlayedAt": 1788249193447
  4462 + "lastPlayedAt": 1788252124992
4450 4463 },
4451 4464 {
4452 4465 "id": "807e042dce0d",
... ... @@ -4461,7 +4474,7 @@
4461 4474 "likes": 0,
4462 4475 "createdAt": 1787290119529,
4463 4476 "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella",
4464   - "lastPlayedAt": 1788249287492
  4477 + "lastPlayedAt": 1788251302490
4465 4478 },
4466 4479 {
4467 4480 "id": "06bb43196c46",
... ... @@ -4506,7 +4519,7 @@
4506 4519 "likes": 0,
4507 4520 "createdAt": 1787231456079,
4508 4521 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own",
4509   - "lastPlayedAt": 1788246433257
  4522 + "lastPlayedAt": 1788251682774
4510 4523 },
4511 4524 {
4512 4525 "id": "66158f543a20",
... ... @@ -4521,7 +4534,7 @@
4521 4534 "likes": 0,
4522 4535 "createdAt": 1787231440149,
4523 4536 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own",
4524   - "lastPlayedAt": 1788246941650
  4537 + "lastPlayedAt": 1788250606173
4525 4538 },
4526 4539 {
4527 4540 "id": "63c91f4b6bc7",
... ... @@ -4550,7 +4563,7 @@
4550 4563 "partition": "fl2va",
4551 4564 "likes": 0,
4552 4565 "createdAt": 1787224701504,
4553   - "lastPlayedAt": 1788247614231,
  4566 + "lastPlayedAt": 1788252757232,
4554 4567 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own"
4555 4568 },
4556 4569 {
... ... @@ -4566,7 +4579,7 @@
4566 4579 "likes": 0,
4567 4580 "createdAt": 1787224519456,
4568 4581 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own",
4569   - "lastPlayedAt": 1788244245424
  4582 + "lastPlayedAt": 1788252627218
4570 4583 },
4571 4584 {
4572 4585 "id": "a1cc621cc6b2",
... ... @@ -4581,6 +4594,6 @@
4581 4594 "likes": 0,
4582 4595 "createdAt": 1787220579189,
4583 4596 "promptEn": "gothic castle at night, vampire noble carries a princess, rose petals falling",
4584   - "lastPlayedAt": 1788248725194
  4597 + "lastPlayedAt": 1788252092980
4585 4598 }
4586 4599 ]
4587 4600 \ No newline at end of file
... ...
live.css
1 1 * { box-sizing: border-box; }
2 2 [hidden] { display: none !important; }
3 3  
4   -/* Palette + type lifted from infiniteslop.ai: pure black, hot-pink accent,
5   - Tahoma body copy, Shrikhand display logo with a punchy drop-shadow. */
  4 +/* Palette + type lifted from infiniteslop.ai: pure black canvas, Tahoma bold
  5 + everywhere, hot-pink accent, and a hand-drawn "comic panel" look — white
  6 + boxes with a thick black border and a hard offset drop-shadow (no blur) —
  7 + for every interactive surface (buttons, bubbles, prompt cards). */
6 8 :root {
7 9 --bg: #000;
  10 + --panel: #000;
  11 + --divider: rgba(255, 255, 255, .08);
8 12 --text: #fff;
9   - --muted: #9a9aa1;
10   - --border: #1e1e22;
11   - --panel: #0e0e11;
12   - --row-hover: #17171a;
  13 + --muted: rgba(255, 255, 255, .55);
  14 + --ink: #111;
13 15 --accent: #ff90e8;
14   - --accent-text: #111;
  16 + --accent-text: #d81b9a;
  17 + --gen-bg: #ffe990;
  18 + --shadow-sm: 3px 3px 0 #000;
  19 + --shadow: 4px 4px 0 #000;
  20 + --shadow-lg: 5px 5px 0 #000;
15 21 }
16 22  
17 23 body {
... ... @@ -31,34 +37,56 @@ body {
31 37 .topbar {
32 38 display: flex;
33 39 align-items: center;
34   - gap: 14px;
  40 + gap: 10px;
35 41 padding: 10px 16px;
36   - border-bottom: 1px solid var(--border);
37 42 flex: 0 0 auto;
38 43 }
39 44 .topbar .logo {
40 45 font-family: "Shrikhand", "Cooper Black", serif;
41 46 font-weight: 400;
42   - font-size: 20px;
  47 + font-size: 22px;
43 48 margin: 0;
44 49 color: #fff;
45   - text-shadow: 2px 2px 0 var(--accent);
  50 + -webkit-text-stroke: 2px #000;
  51 + paint-order: stroke fill;
  52 + text-shadow: 1px 1px 0 #000, 2px 2px 0 #000, 3px 3px 0 #000;
46 53 }
47 54 .topbar .spacer { flex: 1; }
48   -.watching { font-size: 12px; font-weight: 700; color: var(--muted); }
  55 +.watching {
  56 + font: bold 13px Tahoma, sans-serif;
  57 + padding: 6px 12px;
  58 + border: 3px solid #000;
  59 + border-radius: 12px;
  60 + background: #fff;
  61 + color: var(--ink);
  62 + box-shadow: var(--shadow);
  63 +}
  64 +.topbar-pill {
  65 + flex: none;
  66 + font: bold 13px Tahoma, sans-serif;
  67 + cursor: pointer;
  68 + padding: 6px 12px;
  69 + border: 3px solid #000;
  70 + border-radius: 12px;
  71 + background: #fff;
  72 + color: var(--ink);
  73 + box-shadow: var(--shadow);
  74 +}
  75 +.topbar-pill:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 #000; }
49 76  
50   -.tabs { display: flex; gap: 6px; }
  77 +.tabs { display: flex; gap: 8px; }
51 78 .tabs button {
  79 + font: bold 12px Tahoma, sans-serif;
52 80 background: #fff;
53   - border: none;
54   - color: var(--accent-text);
55   - border-radius: 999px;
  81 + border: 3px solid #000;
  82 + color: var(--ink);
  83 + border-radius: 12px;
56 84 padding: 6px 14px;
57   - font-size: 12px;
58   - font-weight: 700;
  85 + box-shadow: var(--shadow);
59 86 cursor: pointer;
60 87 }
61   -.tabs button.active { background: var(--accent); color: var(--accent-text); }
  88 +.tabs button:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 #000; }
  89 +.tabs button.active { background: var(--accent); }
62 90  
63 91 .body {
64 92 flex: 1;
... ... @@ -68,41 +96,94 @@ body {
68 96  
69 97 /* ---- left: queue / browse list ---- */
70 98 .left-col {
71   - width: 300px;
72   - min-width: 240px;
73   - border-right: 1px solid var(--border);
  99 + width: 320px;
  100 + min-width: 260px;
  101 + border-right: 1px solid var(--divider);
74 102 display: flex;
75 103 flex-direction: column;
76 104 overflow: hidden;
77 105 background: var(--panel);
  106 + padding: 12px 10px;
  107 + gap: 8px;
78 108 }
79   -.left-col .section-title {
80   - font-size: 11px;
81   - font-weight: 700;
82   - letter-spacing: .06em;
  109 +.section-head {
  110 + font: bold 13px Tahoma, sans-serif;
  111 + color: #fff;
  112 + text-shadow: 0 1px 3px #000;
  113 + letter-spacing: .5px;
83 114 text-transform: uppercase;
84   - color: var(--muted);
85   - padding: 12px 14px 6px;
  115 + margin: 10px 2px 2px;
86 116 }
87   -.clip-list { flex: 1; overflow-y: auto; }
  117 +.section-head:first-child { margin-top: 2px; }
  118 +.clip-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.3) transparent; }
  119 +.clip-list::-webkit-scrollbar { width: 6px; }
  120 +.clip-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,.3); border-radius: 8px; }
  121 +
88 122 .clip-row {
89   - padding: 10px 14px;
90   - border-bottom: 1px solid var(--border);
  123 + align-self: flex-start;
  124 + width: 100%;
  125 + color: var(--ink);
  126 + background: #fff;
  127 + border: 3px solid #000;
  128 + border-radius: 16px;
  129 + padding: 8px 14px;
  130 + box-shadow: var(--shadow-lg);
91 131 cursor: pointer;
92 132 }
93   -.clip-row:hover { background: var(--row-hover); }
94   -.clip-row.active { background: var(--row-hover); box-shadow: inset 3px 0 0 var(--accent); }
  133 +.clip-row.gen { background: var(--gen-bg); cursor: default; }
  134 +.clip-row.failed { background: #ffd2d2; cursor: default; }
  135 +.clip-row.active { outline: 3px solid var(--accent-text); outline-offset: 2px; }
95 136 .clip-row .prompt {
96   - font-size: 12.5px;
  137 + font-size: 13.5px;
  138 + font-weight: bold;
97 139 line-height: 1.4;
98   - color: #d8d8dd;
99 140 display: -webkit-box;
100 141 -webkit-line-clamp: 2;
101 142 -webkit-box-orient: vertical;
102 143 overflow: hidden;
103 144 }
104   -.clip-row .meta { font-size: 10.5px; color: #6b6b72; margin-top: 4px; }
105   -.empty-hint { color: #6b6b72; text-align: center; margin-top: 40px; font-size: 13px; }
  145 +.clip-row .meta { font-size: 11px; font-weight: bold; opacity: .6; margin-top: 3px; }
  146 +
  147 +.qrow { display: flex; align-items: flex-start; gap: 8px; }
  148 +.qrow .clip-row { flex: 1; min-width: 0; cursor: default; }
  149 +.up-btn {
  150 + flex: none;
  151 + cursor: pointer;
  152 + font: bold 12px Tahoma, sans-serif;
  153 + color: var(--ink);
  154 + background: #fff;
  155 + border: 3px solid #000;
  156 + border-radius: 12px;
  157 + box-shadow: var(--shadow-sm);
  158 + padding: 6px 8px;
  159 + display: flex;
  160 + flex-direction: column;
  161 + align-items: center;
  162 + line-height: 1;
  163 + min-width: 36px;
  164 +}
  165 +.up-btn span { margin-top: 3px; color: var(--accent-text); font-variant-numeric: tabular-nums; }
  166 +.up-btn.voted { background: var(--accent); }
  167 +.up-btn:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 #000; }
  168 +.up-btn:disabled { opacity: .6; cursor: default; }
  169 +
  170 +.empty-hint { color: var(--muted); text-align: center; margin-top: 40px; font-size: 13px; font-weight: bold; }
  171 +
  172 +.site-credit {
  173 + flex: none;
  174 + display: inline-flex;
  175 + align-items: center;
  176 + gap: 6px;
  177 + align-self: flex-start;
  178 + font: bold 14px Tahoma, sans-serif;
  179 + padding: 7px 14px;
  180 + border: 3px solid #000;
  181 + border-radius: 12px;
  182 + background: #fff;
  183 + color: var(--ink);
  184 + box-shadow: var(--shadow);
  185 +}
  186 +.site-credit span { color: var(--accent-text); }
106 187  
107 188 /* ---- center: player, no native controls — pure "watch only" ---- */
108 189 .player-col {
... ... @@ -128,113 +209,147 @@ body {
128 209 object-fit: contain;
129 210 }
130 211  
131   -.now-playing-bar {
  212 +.live-pill {
132 213 position: absolute;
133   - top: 0;
134   - left: 0;
135   - right: 0;
  214 + left: 50%;
  215 + top: 14px;
  216 + transform: translateX(-50%);
136 217 z-index: 2;
137   - padding: 12px 16px;
138   - font-size: 13px;
139   - font-weight: 700;
140   - line-height: 1.4;
  218 + display: inline-flex;
  219 + align-items: center;
  220 + gap: 7px;
  221 + border: none;
141 222 color: #fff;
142   - background: linear-gradient(to bottom, rgba(0,0,0,.75), rgba(0,0,0,0));
  223 + font: 700 12px/1 Tahoma, sans-serif;
  224 + letter-spacing: .4px;
  225 + padding: 8px 15px;
  226 + border-radius: 999px;
  227 + background: #e11d2a;
  228 + box-shadow: 0 3px 12px rgba(0, 0, 0, .45);
143 229 }
144   -.now-playing-bar #liveDot { color: #ff3b3b; font-weight: 700; margin-right: 4px; }
  230 +.live-pill .dot { width: 7px; height: 7px; border-radius: 50%; background: #fff; animation: livedot 1.4s ease-in-out infinite; }
  231 +@keyframes livedot { 50% { opacity: .25; } }
145 232  
146   -.mute-btn {
  233 +.now-playing-box {
147 234 position: absolute;
148   - bottom: 14px;
149   - right: 14px;
  235 + top: 14px;
  236 + left: 14px;
150 237 z-index: 2;
  238 + max-width: min(560px, calc(100% - 28px));
151 239 background: #fff;
152   - border: none;
153   - color: var(--accent-text);
154   - border-radius: 999px;
155   - width: 36px;
156   - height: 36px;
157   - font-size: 15px;
158   - font-weight: 700;
159   - cursor: pointer;
  240 + color: var(--ink);
  241 + border: 3px solid #000;
  242 + border-radius: 12px;
  243 + padding: 8px 14px;
  244 + box-shadow: var(--shadow);
  245 + font-size: 13px;
  246 + line-height: 1.4;
160 247 }
  248 +.now-playing-box .np-nick { font-weight: bold; color: var(--accent-text); margin-right: 6px; }
  249 +.now-playing-box .np-time { opacity: .5; font-variant-numeric: tabular-nums; }
  250 +.now-playing-box .np-text { font-weight: bold; margin-top: 2px; }
161 251  
162   -/* ---- right: prompt input ---- */
  252 +/* ---- right: chat ---- */
163 253 .right-col {
164   - width: 300px;
165   - min-width: 240px;
166   - border-left: 1px solid var(--border);
  254 + width: 320px;
  255 + min-width: 260px;
  256 + border-left: 1px solid var(--divider);
167 257 display: flex;
168 258 flex-direction: column;
169 259 background: var(--panel);
  260 + padding: 12px 10px 10px;
170 261 }
171 262 .right-col .section-title {
172   - font-size: 11px;
173   - font-weight: 700;
174   - letter-spacing: .06em;
  263 + font: bold 13px Tahoma, sans-serif;
  264 + letter-spacing: .5px;
175 265 text-transform: uppercase;
176   - color: var(--muted);
177   - padding: 12px 14px 6px;
  266 + color: #fff;
  267 + text-shadow: 0 1px 3px #000;
  268 + padding: 0 4px 8px;
  269 +}
  270 +.chat-list {
  271 + flex: 1;
  272 + overflow-y: auto;
  273 + overflow-x: hidden;
  274 + padding: 3px 4px;
  275 + display: flex;
  276 + flex-direction: column;
  277 + gap: 10px;
  278 + scrollbar-width: thin;
  279 + scrollbar-color: rgba(255, 255, 255, .3) transparent;
  280 + -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 40px);
  281 + mask-image: linear-gradient(to bottom, transparent 0, #000 40px);
  282 +}
  283 +.chat-list::-webkit-scrollbar { width: 6px; }
  284 +.chat-list::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .3); border-radius: 8px; }
  285 +.chat-list .chat-msg:first-child { margin-top: auto; }
  286 +
  287 +.chat-msg {
  288 + align-self: flex-start;
  289 + max-width: 92%;
  290 + color: var(--ink);
  291 + background: #fff;
  292 + border: 3px solid #000;
  293 + border-radius: 16px;
  294 + padding: 7px 14px;
  295 + font-size: 14px;
  296 + line-height: 1.4;
  297 + box-shadow: var(--shadow-lg);
  298 + word-break: break-word;
178 299 }
  300 +.chat-nick { margin-right: 6px; }
  301 +.chat-status { display: block; font-size: 11px; opacity: .6; font-weight: bold; margin-top: 2px; }
  302 +
179 303 .chat-identity-row {
180 304 display: flex;
181 305 align-items: center;
  306 + justify-content: space-between;
182 307 gap: 8px;
183   - padding: 0 14px 6px;
184   - font-size: 11.5px;
185   - font-weight: 700;
  308 + padding: 8px 4px 2px;
  309 + font-size: 12px;
  310 + font-weight: bold;
  311 + color: var(--muted);
186 312 }
187   -.chat-identity-row #currentNickname { color: var(--accent); }
  313 +.chat-identity-row #currentNickname { color: var(--accent-text); }
188 314 .chat-identity-row #changeNameBtn {
189 315 background: transparent;
190 316 border: none;
191 317 color: var(--muted);
192 318 font-size: 11px;
193   - font-weight: 700;
194   - text-decoration: underline;
  319 + font-weight: bold;
195 320 cursor: pointer;
  321 + text-decoration: underline;
196 322 padding: 0;
197 323 }
198 324 .chat-identity-row #changeNameBtn:hover { color: #fff; }
199   -.chat-list {
200   - flex: 1;
201   - overflow-y: auto;
202   - padding: 0 14px;
203   - display: flex;
204   - flex-direction: column;
205   - gap: 8px;
206   -}
207   -.chat-msg { font-size: 12.5px; line-height: 1.5; word-break: break-word; }
208   -.chat-nick { color: var(--accent); font-weight: 700; margin-right: 6px; }
209   -.chat-text { color: #e4e4e8; }
210   -.chat-status { display: block; font-size: 10.5px; color: var(--muted); font-weight: 700; margin-top: 1px; }
211 325  
212   -.chat-input-row { display: flex; gap: 6px; padding: 10px 14px 4px; }
  326 +.chat-input-row { display: flex; gap: 8px; padding: 8px 4px 4px; }
213 327 .chat-input-row input {
214 328 flex: 1;
215 329 min-width: 0;
  330 + font: bold 14px Tahoma, sans-serif;
  331 + padding: 9px 13px;
  332 + border: 3px solid #000;
  333 + border-radius: 12px;
  334 + outline: none;
  335 + box-shadow: var(--shadow);
216 336 background: #fff;
217   - border: none;
218   - border-radius: 999px;
219   - padding: 9px 14px;
220   - color: var(--accent-text);
221   - font-weight: 700;
222   - font-size: 13px;
223   - font-family: inherit;
  337 + color: var(--ink);
224 338 }
225   -.chat-input-row input::placeholder { color: #8a8a8a; font-weight: 400; }
  339 +.chat-input-row input::placeholder { color: #888; font-weight: normal; }
226 340 .chat-input-row button {
227   - background: var(--accent);
228   - color: var(--accent-text);
229   - border: none;
230   - border-radius: 999px;
231   - padding: 0 16px;
232   - font-weight: 700;
233   - font-size: 13px;
  341 + font: bold 14px Tahoma, sans-serif;
234 342 cursor: pointer;
  343 + padding: 9px 16px;
  344 + border: 3px solid #000;
  345 + border-radius: 12px;
  346 + background: var(--accent);
  347 + color: var(--ink);
  348 + box-shadow: var(--shadow);
235 349 }
  350 +.chat-input-row button:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 #000; }
236 351 .chat-input-row button:disabled { opacity: .5; cursor: not-allowed; }
237   -.say-hint { padding: 0 14px 10px; font-size: 11.5px; font-weight: 700; color: var(--muted); min-height: 1.4em; }
  352 +.say-hint { padding: 2px 4px 0; font-size: 11.5px; font-weight: bold; color: var(--muted); min-height: 1.4em; }
238 353  
239 354 /* ---- nickname modal ---- */
240 355 .nickname-modal {
... ... @@ -244,57 +359,66 @@ body {
244 359 display: flex;
245 360 align-items: center;
246 361 justify-content: center;
247   - background: rgba(0,0,0,.7);
  362 + background: rgba(0, 0, 0, .7);
248 363 }
249 364 .nickname-box {
250 365 background: #fff;
251   - color: var(--accent-text);
252   - border-radius: 16px;
253   - padding: 20px;
254   - width: 260px;
  366 + color: var(--ink);
  367 + border: 3px solid #000;
  368 + border-radius: 12px;
  369 + box-shadow: 6px 6px 0 #000;
  370 + padding: 22px;
  371 + width: 280px;
255 372 display: flex;
256 373 flex-direction: column;
257   - gap: 10px;
  374 + gap: 12px;
258 375 }
259   -.nickname-title { font-weight: 700; font-size: 14px; }
  376 +.nickname-title { font-weight: bold; font-size: 15px; }
260 377 .nickname-box input {
261   - border: 1px solid #ddd;
262   - border-radius: 10px;
263   - padding: 8px 12px;
264   - font-size: 13px;
265   - font-family: inherit;
  378 + font: bold 16px Tahoma, sans-serif;
  379 + border: 3px solid #000;
  380 + border-radius: 12px;
  381 + padding: 9px 13px;
  382 + box-shadow: var(--shadow);
  383 + outline: none;
266 384 }
267 385 .nickname-box button {
  386 + font: bold 15px Tahoma, sans-serif;
268 387 background: var(--accent);
269   - color: var(--accent-text);
270   - border: none;
271   - border-radius: 999px;
  388 + color: var(--ink);
  389 + border: 3px solid #000;
  390 + border-radius: 12px;
272 391 padding: 9px 0;
273   - font-weight: 700;
274   - font-size: 13px;
  392 + box-shadow: var(--shadow);
275 393 cursor: pointer;
276 394 }
  395 +.nickname-box button:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 #000; }
277 396  
278 397 /* ---- mobile sub-tabs (live mode only) ---- */
279 398 .mobile-subtabs {
280 399 display: none;
281   - border-top: 1px solid var(--border);
  400 + border-top: 1px solid var(--divider);
282 401 flex: 0 0 auto;
283 402 background: var(--panel);
  403 + gap: 8px;
  404 + padding: 8px;
284 405 }
285 406 .mobile-subtabs button {
286 407 flex: 1;
287   - background: transparent;
288   - border: none;
289   - color: var(--muted);
290   - font-weight: 700;
291   - padding: 11px 0;
292   - font-size: 13px;
  408 + font: bold 13px Tahoma, sans-serif;
  409 + background: #fff;
  410 + border: 3px solid #000;
  411 + color: var(--ink);
  412 + border-radius: 12px;
  413 + padding: 9px 0;
  414 + box-shadow: var(--shadow-sm);
293 415 cursor: pointer;
294 416 }
295   -.mobile-subtabs button.active { color: #fff; box-shadow: inset 0 -2px 0 var(--accent); }
  417 +.mobile-subtabs button.active { background: var(--accent); }
296 418  
297 419 @media (max-width: 768px) {
  420 + .topbar { flex-wrap: wrap; row-gap: 8px; }
  421 + .topbar .logo { font-size: 18px; }
298 422 .body { flex-direction: column; }
299 423 .player-col { order: 1; flex: 0 0 46vh; }
300 424 .mobile-subtabs { display: flex; order: 2; }
... ... @@ -306,4 +430,5 @@ body {
306 430 flex: 1;
307 431 }
308 432 .left-col.mobile-inactive, .right-col.mobile-inactive { display: none; }
  433 + .now-playing-box { font-size: 11px; max-width: calc(100% - 28px); }
309 434 }
... ...
live.html
... ... @@ -20,22 +20,25 @@
20 20 </div>
21 21 <div class="spacer"></div>
22 22 <div class="watching" id="watchingCount" hidden></div>
  23 + <button class="topbar-pill" id="chatToggleBtn">✕ 聊天</button>
  24 + <button class="topbar-pill" id="muteBtn">🔇 静音</button>
23 25 </header>
24 26  
25 27 <div class="body" id="body">
26 28 <aside class="left-col" id="leftCol">
27   - <div class="section-title" id="listTitle">片段列表</div>
28 29 <div class="clip-list" id="clipList"></div>
  30 + <div class="site-credit">🍋 <span>AI 短剧频道</span></div>
29 31 </aside>
30 32  
31 33 <div class="player-col">
32 34 <div class="player-wrap">
33   - <div class="now-playing-bar">
34   - <span id="liveDot" hidden>🔴 直播中 ·</span>
35   - <span id="nowPlayingText">加载中…</span>
  35 + <div class="live-pill" id="liveDot" hidden><span class="dot"></span>直播中</div>
  36 + <div class="now-playing-box" id="nowPlayingBox" hidden>
  37 + <span class="np-nick" id="nowPlayingNick"></span>
  38 + <span class="np-time" id="nowPlayingTime"></span>
  39 + <div class="np-text" id="nowPlayingText">加载中…</div>
36 40 </div>
37 41 <video id="player" autoplay muted playsinline></video>
38   - <button class="mute-btn" id="muteBtn">🔇</button>
39 42 </div>
40 43 </div>
41 44  
... ... @@ -44,7 +47,7 @@
44 47 <div class="chat-list" id="chatList"></div>
45 48 <div class="chat-identity-row" id="chatIdentityRow" hidden>
46 49 <span id="currentNickname"></span>
47   - <button id="changeNameBtn">change name</button>
  50 + <button id="changeNameBtn">改昵称</button>
48 51 </div>
49 52 <div class="chat-input-row">
50 53 <input type="text" id="chatInput" placeholder="说点什么,或者想看什么…" maxlength="300" />
... ... @@ -83,7 +86,9 @@ let mobileSub = &quot;queue&quot;;
83 86  
84 87 const player = document.getElementById("player");
85 88 const clipList = document.getElementById("clipList");
86   -const listTitle = document.getElementById("listTitle");
  89 +const nowPlayingBox = document.getElementById("nowPlayingBox");
  90 +const nowPlayingNick = document.getElementById("nowPlayingNick");
  91 +const nowPlayingTime = document.getElementById("nowPlayingTime");
87 92 const nowPlayingText = document.getElementById("nowPlayingText");
88 93 const liveDot = document.getElementById("liveDot");
89 94 const leftCol = document.getElementById("leftCol");
... ... @@ -95,6 +100,7 @@ const chatSendBtn = document.getElementById(&quot;chatSendBtn&quot;);
95 100 const chatHint = document.getElementById("chatHint");
96 101 const watchingCount = document.getElementById("watchingCount");
97 102 const muteBtn = document.getElementById("muteBtn");
  103 +const chatToggleBtn = document.getElementById("chatToggleBtn");
98 104 const nicknameModal = document.getElementById("nicknameModal");
99 105 const nicknameInput = document.getElementById("nicknameInput");
100 106 const nicknameConfirmBtn = document.getElementById("nicknameConfirmBtn");
... ... @@ -105,7 +111,14 @@ const clientId = Math.random().toString(36).slice(2);
105 111  
106 112 muteBtn.addEventListener("click", () => {
107 113 player.muted = !player.muted;
108   - muteBtn.textContent = player.muted ? "🔇" : "🔈";
  114 + muteBtn.textContent = player.muted ? "🔇 静音" : "🔈 有声";
  115 +});
  116 +
  117 +let chatClosed = false;
  118 +chatToggleBtn.addEventListener("click", () => {
  119 + chatClosed = !chatClosed;
  120 + chatToggleBtn.textContent = chatClosed ? "聊天" : "✕ 聊天";
  121 + if (mode === "live") rightCol.hidden = chatClosed;
109 122 });
110 123  
111 124 function fmtMeta(c) {
... ... @@ -116,6 +129,16 @@ function escapeHtml(s) {
116 129 div.textContent = s || "";
117 130 return div.innerHTML;
118 131 }
  132 +function fmtClock(ts) {
  133 + return new Date(ts).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
  134 +}
  135 +// Same per-nickname pastel-hash used by infiniteslop.ai — a stable, deterministic
  136 +// color per person without needing a server-assigned palette.
  137 +function pastel(name) {
  138 + let h = 0;
  139 + for (const c of name || "") h = (h * 31 + c.charCodeAt(0)) % 360;
  140 + return `hsl(${h},85%,85%)`;
  141 +}
119 142  
120 143 // ---- viewer identity: nickname + uid, stored in cookies ----
121 144 function getCookie(name) {
... ... @@ -187,10 +210,10 @@ updateNicknameLabel();
187 210  
188 211 function renderBrowseList() {
189 212 if (clips.length === 0) {
190   - clipList.innerHTML = `<div class="empty-hint">片段库为空</div>`;
  213 + clipList.innerHTML = `<div class="section-head">片段列表</div><div class="empty-hint">片段库为空</div>`;
191 214 return;
192 215 }
193   - clipList.innerHTML = clips
  216 + const rows = clips
194 217 .map(
195 218 (c, i) => `
196 219 <div class="clip-row ${i === currentIndex ? "active" : ""}" data-i="${i}">
... ... @@ -199,6 +222,7 @@ function renderBrowseList() {
199 222 </div>`
200 223 )
201 224 .join("");
  225 + clipList.innerHTML = `<div class="section-head">片段列表</div>${rows}`;
202 226 clipList.querySelectorAll(".clip-row").forEach((row) => {
203 227 row.addEventListener("click", (e) => {
204 228 if (e.target.dataset.likes) return;
... ... @@ -215,13 +239,22 @@ function renderBrowseList() {
215 239 });
216 240 }
217 241  
  242 +function setNowPlaying(nick, time, text) {
  243 + nowPlayingBox.hidden = false;
  244 + nowPlayingNick.hidden = !nick;
  245 + nowPlayingNick.textContent = nick || "";
  246 + nowPlayingTime.hidden = !time;
  247 + nowPlayingTime.textContent = time || "";
  248 + nowPlayingText.textContent = text;
  249 +}
  250 +
218 251 function playBrowse(index) {
219 252 if (index < 0 || index >= clips.length) return;
220 253 currentIndex = index;
221 254 const c = clips[index];
222 255 player.src = `/lib/video/${c.id}`;
223 256 player.play().catch(() => {});
224   - nowPlayingText.textContent = c.prompt;
  257 + setNowPlaying(null, null, c.prompt);
225 258 renderBrowseList();
226 259 }
227 260  
... ... @@ -237,34 +270,85 @@ async function loadBrowse(sort) {
237 270 if (clips.length) playBrowse(0);
238 271 }
239 272  
240   -// Left queue: prompt + who asked for it only — no resolution/duration/date.
  273 +// Left queue, grouped like infiniteslop.ai: 接下来播放 (already scheduled) /
  274 +// 生成中 (mid-generation) / 排队中 (waiting their turn — votable).
241 275 function renderLiveList(state) {
242   - listTitle.textContent = "排队队列";
243 276 const upcoming = state.upNext || [];
244 277 const queue = state.queue || [];
245   - const statusLabel = { generating: "生成中…", matched: "即将播放", failed: "❌ 生成失败" };
246   - const queueHtml = queue
  278 + const generating = queue.filter((q) => q.status === "generating");
  279 + const waiting = queue.filter((q) => q.status === "matched" || q.status === "ready");
  280 + const failed = queue.filter((q) => q.status === "failed");
  281 +
  282 + const upcomingHtml = upcoming
247 283 .map(
248   - (q) => `
  284 + (c) => `
249 285 <div class="clip-row">
  286 + <div class="prompt">${escapeHtml(c.prompt)}</div>
  287 + ${c.requestedBy ? `<div class="meta">by ${escapeHtml(c.requestedBy)}</div>` : ""}
  288 + </div>`
  289 + )
  290 + .join("");
  291 + const generatingHtml = generating
  292 + .map(
  293 + (q) => `
  294 + <div class="clip-row gen">
250 295 <div class="prompt">💬 ${escapeHtml(q.prompt)}</div>
251   - <div class="meta">${q.nickname ? `${escapeHtml(q.nickname)} · ` : ""}${statusLabel[q.status] || q.status}</div>
  296 + <div class="meta">${q.nickname ? `${escapeHtml(q.nickname)} · ` : ""}生成中…</div>
252 297 </div>`
253 298 )
254 299 .join("");
255   - const upcomingHtml = upcoming
  300 + const waitingHtml = waiting
256 301 .map(
257   - (c) => `
258   - <div class="clip-row">
259   - <div class="prompt">${escapeHtml(c.prompt)}</div>
260   - ${c.requestedBy ? `<div class="meta">by ${escapeHtml(c.requestedBy)}</div>` : ""}
  302 + (q) => `
  303 + <div class="qrow">
  304 + <div class="clip-row">
  305 + <div class="prompt">💬 ${escapeHtml(q.prompt)}</div>
  306 + <div class="meta">${q.nickname ? `${escapeHtml(q.nickname)} · ` : ""}即将播放</div>
  307 + </div>
  308 + <button class="up-btn ${q.voted ? "voted" : ""}" data-vote="${q.id}">▲<span>${q.votes || 0}</span></button>
261 309 </div>`
262 310 )
263 311 .join("");
264   - clipList.innerHTML = queueHtml + upcomingHtml || `<div class="empty-hint">暂无排队</div>`;
  312 + const failedHtml = failed
  313 + .map(
  314 + (q) => `
  315 + <div class="clip-row failed">
  316 + <div class="prompt">💬 ${escapeHtml(q.prompt)}</div>
  317 + <div class="meta">${q.nickname ? `${escapeHtml(q.nickname)} · ` : ""}❌ 生成失败</div>
  318 + </div>`
  319 + )
  320 + .join("");
  321 +
  322 + clipList.innerHTML =
  323 + (upcomingHtml ? `<div class="section-head">接下来播放</div>${upcomingHtml}` : "") +
  324 + (generatingHtml ? `<div class="section-head">生成中</div>${generatingHtml}` : "") +
  325 + (waitingHtml || failedHtml ? `<div class="section-head">排队中</div>${waitingHtml}${failedHtml}` : "") ||
  326 + `<div class="empty-hint">暂无排队</div>`;
  327 +
  328 + clipList.querySelectorAll("[data-vote]").forEach((btn) => {
  329 + btn.addEventListener("click", () => voteQueueItem(btn.dataset.vote, btn));
  330 + });
  331 +}
  332 +
  333 +async function voteQueueItem(id, btn) {
  334 + const identity = await ensureIdentity();
  335 + btn.disabled = true;
  336 + try {
  337 + const res = await fetch(`/api/queue/vote/${id}`, {
  338 + method: "POST",
  339 + headers: { "Content-Type": "application/json" },
  340 + body: JSON.stringify({ uid: identity.uid }),
  341 + });
  342 + if (!res.ok) return;
  343 + const data = await res.json();
  344 + btn.classList.toggle("voted", data.voted);
  345 + btn.querySelector("span").textContent = data.votes;
  346 + } finally {
  347 + btn.disabled = false;
  348 + }
265 349 }
266 350  
267   -function switchToClip(clip, offsetSec) {
  351 +function switchToClip(clip, offsetSec, requestedBy) {
268 352 currentClipId = clip.id;
269 353 player.src = `/lib/video/${clip.id}`;
270 354 if (offsetSec > 0) {
... ... @@ -277,12 +361,13 @@ function switchToClip(clip, offsetSec) {
277 361 );
278 362 }
279 363 player.play().catch(() => {});
280   - nowPlayingText.textContent = clip.prompt;
  364 + setNowPlaying(requestedBy, fmtClock(Date.now()), clip.prompt);
281 365 }
282 366  
283 367 async function pollLive() {
284 368 try {
285   - const res = await fetch(`/api/live/state?cid=${clientId}`);
  369 + const identity = getIdentity();
  370 + const res = await fetch(`/api/live/state?cid=${clientId}${identity ? `&uid=${encodeURIComponent(identity.uid)}` : ""}`);
286 371 const state = await res.json();
287 372 if (mode !== "live") return;
288 373 watchingCount.hidden = false;
... ... @@ -294,14 +379,14 @@ async function pollLive() {
294 379 if (state.current) {
295 380 if (state.current.clip.id !== currentClipId) {
296 381 const offsetSec = Math.max(0, (state.now - state.current.startAt) / 1000);
297   - switchToClip(state.current.clip, offsetSec);
  382 + switchToClip(state.current.clip, offsetSec, state.current.requestedBy);
298 383 }
299 384 // Fire the switch to the next clip exactly when this one should end,
300 385 // instead of waiting for the next 4s poll to notice — the poll still
301 386 // runs on schedule and will correct any drift.
302 387 if (upNextCache.length) {
303 388 const msUntilEnd = state.current.endAt - Date.now();
304   - advanceTimer = setTimeout(() => switchToClip(upNextCache[0], 0), Math.max(0, msUntilEnd));
  389 + advanceTimer = setTimeout(() => switchToClip(upNextCache[0], 0, upNextCache[0].requestedBy), Math.max(0, msUntilEnd));
305 390 }
306 391 }
307 392 } catch {
... ... @@ -315,7 +400,7 @@ function renderChat(messages) {
315 400 chatList.innerHTML = messages
316 401 .map((m) => {
317 402 const status = m.status && chatStatusLabel[m.status] ? `<span class="chat-status">${chatStatusLabel[m.status]}</span>` : "";
318   - return `<div class="chat-msg"><span class="chat-nick">${escapeHtml(m.nickname)}</span><span class="chat-text">${escapeHtml(m.text)}</span>${status}</div>`;
  403 + return `<div class="chat-msg" style="background:${pastel(m.nickname)}"><b class="chat-nick">${escapeHtml(m.nickname)}</b><span class="chat-text">${escapeHtml(m.text)}</span>${status}</div>`;
319 404 })
320 405 .join("");
321 406 if (atBottom) chatList.scrollTop = chatList.scrollHeight;
... ... @@ -378,9 +463,10 @@ async function setMode(newMode) {
378 463 clearInterval(chatPollTimer);
379 464 clearTimeout(advanceTimer);
380 465 currentClipId = null;
  466 + chatToggleBtn.hidden = newMode !== "live";
381 467 if (newMode === "live") {
382 468 liveDot.hidden = false;
383   - rightCol.hidden = false;
  469 + rightCol.hidden = chatClosed;
384 470 mobileSubtabs.hidden = false;
385 471 applyMobileSub();
386 472 await Promise.all([pollLive(), pollChat()]);
... ... @@ -392,7 +478,6 @@ async function setMode(newMode) {
392 478 mobileSubtabs.hidden = true;
393 479 watchingCount.hidden = true;
394 480 leftCol.classList.remove("mobile-inactive");
395   - listTitle.textContent = "片段列表";
396 481 await loadBrowse(newMode);
397 482 }
398 483 }
... ...
server.js
... ... @@ -134,7 +134,7 @@ function updateChatStatus(id, status) {
134 134 // e.g. testing) — looks up a library match or kicks off generation, exactly
135 135 // like before chat existed. `nickname` is optional (empty for /api/prompt).
136 136 function submitPromptToQueue(id, prompt, nickname) {
137   - const entry = { id, prompt, nickname: nickname || null, submittedAt: Date.now() };
  137 + const entry = { id, prompt, nickname: nickname || null, submittedAt: Date.now(), votes: [] };
138 138 const match = findLibraryMatch(normalizePrompt(prompt));
139 139 if (match) {
140 140 entry.status = "matched";
... ... @@ -184,13 +184,33 @@ function markPlayed(id) {
184 184 // just grows forever and duplicates the same info.
185 185 // Returns {clip, requestedBy, chatId} — chatId lets the caller flip the
186 186 // originating chat message to "scheduled" once it's actually about to air.
  187 +// Among entries of the same status, the most-upvoted prompt goes first
  188 +// (ties broken by submission order) — lets chat bump a favorite to the
  189 +// front of the line instead of strict FIFO.
  190 +function voteCount(q) {
  191 + return (q.votes || []).length;
  192 +}
  193 +function pickBestIndexByStatus(status) {
  194 + let bestIdx = -1;
  195 + for (let i = 0; i < promptQueue.length; i++) {
  196 + const q = promptQueue[i];
  197 + if (q.status !== status) continue;
  198 + if (bestIdx === -1) {
  199 + bestIdx = i;
  200 + continue;
  201 + }
  202 + const best = promptQueue[bestIdx];
  203 + if (voteCount(q) > voteCount(best)) bestIdx = i;
  204 + }
  205 + return bestIdx;
  206 +}
187 207 function pickNextClip() {
188   - const readyIdx = promptQueue.findIndex((q) => q.status === "ready");
  208 + const readyIdx = pickBestIndexByStatus("ready");
189 209 if (readyIdx !== -1) {
190 210 const q = promptQueue.splice(readyIdx, 1)[0];
191 211 return { clip: q.clip, requestedBy: q.nickname, chatId: q.id };
192 212 }
193   - const matchedIdx = promptQueue.findIndex((q) => q.status === "matched");
  213 + const matchedIdx = pickBestIndexByStatus("matched");
194 214 if (matchedIdx !== -1) {
195 215 const q = promptQueue.splice(matchedIdx, 1)[0];
196 216 return { clip: q.clip, requestedBy: q.nickname, chatId: q.id };
... ... @@ -356,7 +376,9 @@ http
356 376 if (pathname === "/api/live/state") {
357 377 ensureSchedule();
358 378 const now = Date.now();
359   - const cid = new URL(req.url, "http://x").searchParams.get("cid");
  379 + const params = new URL(req.url, "http://x").searchParams;
  380 + const cid = params.get("cid");
  381 + const uid = params.get("uid") || null;
360 382 if (cid) viewers.set(cid, now);
361 383 let idx = schedule.findIndex((s) => s.startAt <= now && now < s.endAt);
362 384 if (idx === -1) idx = schedule.length - 1;
... ... @@ -370,7 +392,17 @@ http
370 392 upNext: schedule.slice(idx + 1, idx + 4).map((s) => ({ ...s.clip, requestedBy: s.requestedBy || null })),
371 393 queue: promptQueue
372 394 .filter((q) => ["matched", "generating", "failed"].includes(q.status))
373   - .map((q) => ({ prompt: q.prompt, nickname: q.nickname, status: q.status, error: q.error })),
  395 + .slice()
  396 + .sort((a, b) => voteCount(b) - voteCount(a))
  397 + .map((q) => ({
  398 + id: q.id,
  399 + prompt: q.prompt,
  400 + nickname: q.nickname,
  401 + status: q.status,
  402 + error: q.error,
  403 + votes: voteCount(q),
  404 + voted: uid ? (q.votes || []).includes(uid) : false,
  405 + })),
374 406 })
375 407 );
376 408 return;
... ... @@ -546,6 +578,38 @@ http
546 578 return;
547 579 }
548 580  
  581 + if (pathname.startsWith("/api/queue/vote/") && req.method === "POST") {
  582 + const id = pathname.slice("/api/queue/vote/".length);
  583 + readJsonBody(req)
  584 + .then((body) => {
  585 + const uid = (body.uid || "").trim();
  586 + if (!uid) {
  587 + res.writeHead(400, { "Content-Type": "application/json" });
  588 + res.end(JSON.stringify({ error: "uid required" }));
  589 + return;
  590 + }
  591 + const entry = promptQueue.find((q) => q.id === id);
  592 + if (!entry || !["matched", "generating", "ready"].includes(entry.status)) {
  593 + res.writeHead(404, { "Content-Type": "application/json" });
  594 + res.end(JSON.stringify({ error: "not found" }));
  595 + return;
  596 + }
  597 + if (!Array.isArray(entry.votes)) entry.votes = [];
  598 + const i = entry.votes.indexOf(uid);
  599 + const voted = i === -1;
  600 + if (voted) entry.votes.push(uid);
  601 + else entry.votes.splice(i, 1);
  602 + saveLiveState();
  603 + res.writeHead(200, { "Content-Type": "application/json" });
  604 + res.end(JSON.stringify({ votes: entry.votes.length, voted }));
  605 + })
  606 + .catch(() => {
  607 + res.writeHead(400, { "Content-Type": "application/json" });
  608 + res.end(JSON.stringify({ error: "invalid JSON body" }));
  609 + });
  610 + return;
  611 + }
  612 +
549 613 if (pathname.startsWith("/api/like/") && req.method === "POST") {
550 614 const id = pathname.slice("/api/like/".length);
551 615 const lib = getLibrary();
... ...