Commit caca3b43b683ab2960478bbf50405d60d05d2459

Authored by yibo
1 parent 939c7714

Add multi-viewer chat with nickname identity, moderation, and queue attribution

- Cookie-based viewer identity: first chat attempt prompts for a nickname
  (inline modal, not a blocking native prompt()), generates a client-side
  uid, both persisted in cookies for 1 year
- Right panel is now a live chat feed (username + message + status) with
  an input row below it, replacing the old bare prompt textarea
- New POST /api/chat: runs a moderation check (banned-word blocklist —
  ponytail: starter list, not a real content policy, swap in the real one)
  before a message is allowed into the existing prompt-matching/generation
  pipeline; rejected messages still show in the chat log, just flagged
- New GET /api/chat: recent chat history (capped at 300), polled every 3s
- Left queue simplified to prompt + submitter name only — dropped the
  resolution/duration/date metadata that was there before
- schedule/promptQueue now carry `requestedBy`/`nickname` through to
  /api/live/state so the left queue and "now playing" bar can show who
  asked for what
- /api/prompt kept working (now a thin wrapper over the same
  submitPromptToQueue() the chat endpoint uses) for direct API testing

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
library.json
1 [ 1 [
2 { 2 {
  3 + "id": "af0d86637410",
  4 + "source": "h3console",
  5 + "prompt": "叫几个小猫咪和绿巨人一起跳舞",
  6 + "fullPrompt": "叫几个小猫咪和绿巨人一起跳舞",
  7 + "edited": false,
  8 + "width": 832,
  9 + "height": 480,
  10 + "duration": 5,
  11 + "partition": "text",
  12 + "likes": 0,
  13 + "createdAt": 1788239123020
  14 + },
  15 + {
3 "id": "aa1e9123bd35", 16 "id": "aa1e9123bd35",
4 "source": "h3console", 17 "source": "h3console",
5 "prompt": "道士打量古宅,一脸怀疑", 18 "prompt": "道士打量古宅,一脸怀疑",
@@ -11,7 +24,8 @@ @@ -11,7 +24,8 @@
11 "partition": "ref2va", 24 "partition": "ref2va",
12 "likes": 0, 25 "likes": 0,
13 "createdAt": 1788192916199, 26 "createdAt": 1788192916199,
14 - "promptEn": "taoist eyes the old house suspiciously" 27 + "promptEn": "taoist eyes the old house suspiciously",
  28 + "lastPlayedAt": 1788238919277
15 }, 29 },
16 { 30 {
17 "id": "cb5baad5d724", 31 "id": "cb5baad5d724",
@@ -26,7 +40,7 @@ @@ -26,7 +40,7 @@
26 "likes": 0, 40 "likes": 0,
27 "createdAt": 1788192915845, 41 "createdAt": 1788192915845,
28 "promptEn": "scholar watches the room warily", 42 "promptEn": "scholar watches the room warily",
29 - "lastPlayedAt": 1788231313021 43 + "lastPlayedAt": 1788239169402
30 }, 44 },
31 { 45 {
32 "id": "e8cbb73560ef", 46 "id": "e8cbb73560ef",
@@ -41,7 +55,7 @@ @@ -41,7 +55,7 @@
41 "likes": 0, 55 "likes": 0,
42 "createdAt": 1788192915549, 56 "createdAt": 1788192915549,
43 "promptEn": "taoist steels himself to exorcise something", 57 "promptEn": "taoist steels himself to exorcise something",
44 - "lastPlayedAt": 1788232627825 58 + "lastPlayedAt": 1788239125383
45 }, 59 },
46 { 60 {
47 "id": "5b6dcb357792", 61 "id": "5b6dcb357792",
@@ -70,7 +84,7 @@ @@ -70,7 +84,7 @@
70 "likes": 0, 84 "likes": 0,
71 "createdAt": 1788192914949, 85 "createdAt": 1788192914949,
72 "promptEn": "taoist acts tough but is nervous", 86 "promptEn": "taoist acts tough but is nervous",
73 - "lastPlayedAt": 1788232638595 87 + "lastPlayedAt": 1788239063344
74 }, 88 },
75 { 89 {
76 "id": "167ce1bfa285", 90 "id": "167ce1bfa285",
@@ -85,7 +99,7 @@ @@ -85,7 +99,7 @@
85 "likes": 0, 99 "likes": 0,
86 "createdAt": 1788192914047, 100 "createdAt": 1788192914047,
87 "promptEn": "scholar looks scared, wants to flee", 101 "promptEn": "scholar looks scared, wants to flee",
88 - "lastPlayedAt": 1788233032856 102 + "lastPlayedAt": 1788239189657
89 }, 103 },
90 { 104 {
91 "id": "053bc957b4f8", 105 "id": "053bc957b4f8",
@@ -99,7 +113,8 @@ @@ -99,7 +113,8 @@
99 "partition": "ref2va", 113 "partition": "ref2va",
100 "likes": 0, 114 "likes": 0,
101 "createdAt": 1788192913886, 115 "createdAt": 1788192913886,
102 - "promptEn": "taoist panics, freaked out" 116 + "promptEn": "taoist panics, freaked out",
  117 + "lastPlayedAt": 1788237354074
103 }, 118 },
104 { 119 {
105 "id": "45c1136d1e05", 120 "id": "45c1136d1e05",
@@ -113,7 +128,8 @@ @@ -113,7 +128,8 @@
113 "partition": "ref2va", 128 "partition": "ref2va",
114 "likes": 0, 129 "likes": 0,
115 "createdAt": 1788192913551, 130 "createdAt": 1788192913551,
116 - "promptEn": "taoist looking panicked and scared" 131 + "promptEn": "taoist looking panicked and scared",
  132 + "lastPlayedAt": 1788238667095
117 }, 133 },
118 { 134 {
119 "id": "d169d42a67de", 135 "id": "d169d42a67de",
@@ -127,7 +143,8 @@ @@ -127,7 +143,8 @@
127 "partition": "ref2va", 143 "partition": "ref2va",
128 "likes": 0, 144 "likes": 0,
129 "createdAt": 1788192913492, 145 "createdAt": 1788192913492,
130 - "promptEn": "scholar defiant out of desperation" 146 + "promptEn": "scholar defiant out of desperation",
  147 + "lastPlayedAt": 1788237894431
131 }, 148 },
132 { 149 {
133 "id": "2abd2793fae9", 150 "id": "2abd2793fae9",
@@ -142,7 +159,7 @@ @@ -142,7 +159,7 @@
142 "likes": 0, 159 "likes": 0,
143 "createdAt": 1788192913244, 160 "createdAt": 1788192913244,
144 "promptEn": "scholar cornered but still defiant", 161 "promptEn": "scholar cornered but still defiant",
145 - "lastPlayedAt": 1788233292965 162 + "lastPlayedAt": 1788239435546
146 }, 163 },
147 { 164 {
148 "id": "bbf7efb467e0", 165 "id": "bbf7efb467e0",
@@ -157,7 +174,7 @@ @@ -157,7 +174,7 @@
157 "likes": 0, 174 "likes": 0,
158 "createdAt": 1788192912912, 175 "createdAt": 1788192912912,
159 "promptEn": "taoist charmed, hesitating", 176 "promptEn": "taoist charmed, hesitating",
160 - "lastPlayedAt": 1788233054864 177 + "lastPlayedAt": 1788239405537
161 }, 178 },
162 { 179 {
163 "id": "f8da9934c823", 180 "id": "f8da9934c823",
@@ -172,7 +189,7 @@ @@ -172,7 +189,7 @@
172 "likes": 0, 189 "likes": 0,
173 "createdAt": 1788192912819, 190 "createdAt": 1788192912819,
174 "promptEn": "scholar dazed, charmed", 191 "promptEn": "scholar dazed, charmed",
175 - "lastPlayedAt": 1788231297013 192 + "lastPlayedAt": 1788239529590
176 }, 193 },
177 { 194 {
178 "id": "feb96d5be50f", 195 "id": "feb96d5be50f",
@@ -187,7 +204,7 @@ @@ -187,7 +204,7 @@
187 "likes": 0, 204 "likes": 0,
188 "createdAt": 1788191432903, 205 "createdAt": 1788191432903,
189 "promptEn": "woman in white startled, a bit shy", 206 "promptEn": "woman in white startled, a bit shy",
190 - "lastPlayedAt": 1788232706663 207 + "lastPlayedAt": 1788235312284
191 }, 208 },
192 { 209 {
193 "id": "14f482bdae7f", 210 "id": "14f482bdae7f",
@@ -202,7 +219,7 @@ @@ -202,7 +219,7 @@
202 "likes": 0, 219 "likes": 0,
203 "createdAt": 1788191432362, 220 "createdAt": 1788191432362,
204 "promptEn": "woman in white being intimate and alluring", 221 "promptEn": "woman in white being intimate and alluring",
205 - "lastPlayedAt": 1788233385001 222 + "lastPlayedAt": 1788239853614
206 }, 223 },
207 { 224 {
208 "id": "362b15ab1811", 225 "id": "362b15ab1811",
@@ -216,7 +233,8 @@ @@ -216,7 +233,8 @@
216 "partition": "ref2va", 233 "partition": "ref2va",
217 "likes": 0, 234 "likes": 0,
218 "createdAt": 1788191431874, 235 "createdAt": 1788191431874,
219 - "promptEn": "woman in white gently comforting someone" 236 + "promptEn": "woman in white gently comforting someone",
  237 + "lastPlayedAt": 1788239698615
220 }, 238 },
221 { 239 {
222 "id": "a2c515055269", 240 "id": "a2c515055269",
@@ -231,7 +249,7 @@ @@ -231,7 +249,7 @@
231 "likes": 0, 249 "likes": 0,
232 "createdAt": 1788191431505, 250 "createdAt": 1788191431505,
233 "promptEn": "woman in white scared, dodging the question", 251 "promptEn": "woman in white scared, dodging the question",
234 - "lastPlayedAt": 1788233203825 252 + "lastPlayedAt": 1788237426143
235 }, 253 },
236 { 254 {
237 "id": "667978d52305", 255 "id": "667978d52305",
@@ -246,7 +264,7 @@ @@ -246,7 +264,7 @@
246 "likes": 0, 264 "likes": 0,
247 "createdAt": 1788191431016, 265 "createdAt": 1788191431016,
248 "promptEn": "woman in white coldly threatening", 266 "promptEn": "woman in white coldly threatening",
249 - "lastPlayedAt": 1788232575821 267 + "lastPlayedAt": 1788237922460
250 }, 268 },
251 { 269 {
252 "id": "7f5213748d87", 270 "id": "7f5213748d87",
@@ -261,7 +279,7 @@ @@ -261,7 +279,7 @@
261 "likes": 0, 279 "likes": 0,
262 "createdAt": 1788188423583, 280 "createdAt": 1788188423583,
263 "promptEn": "woman in white scared, dodging", 281 "promptEn": "woman in white scared, dodging",
264 - "lastPlayedAt": 1788232370410 282 + "lastPlayedAt": 1788239361511
265 }, 283 },
266 { 284 {
267 "id": "1597db4be561", 285 "id": "1597db4be561",
@@ -275,7 +293,7 @@ @@ -275,7 +293,7 @@
275 "partition": "ref2va", 293 "partition": "ref2va",
276 "likes": 0, 294 "likes": 0,
277 "createdAt": 1788187365789, 295 "createdAt": 1788187365789,
278 - "lastPlayedAt": 1788233018847, 296 + "lastPlayedAt": 1788236567379,
279 "promptEn": "woman in white being gentle and comforting" 297 "promptEn": "woman in white being gentle and comforting"
280 }, 298 },
281 { 299 {
@@ -290,7 +308,7 @@ @@ -290,7 +308,7 @@
290 "partition": "ref2va", 308 "partition": "ref2va",
291 "likes": 0, 309 "likes": 0,
292 "createdAt": 1788186218619, 310 "createdAt": 1788186218619,
293 - "lastPlayedAt": 1788232390420, 311 + "lastPlayedAt": 1788239105368,
294 "promptEn": "woman in white evading, scared" 312 "promptEn": "woman in white evading, scared"
295 }, 313 },
296 { 314 {
@@ -306,7 +324,7 @@ @@ -306,7 +324,7 @@
306 "likes": 0, 324 "likes": 0,
307 "createdAt": 1788185090875, 325 "createdAt": 1788185090875,
308 "promptEn": "woman in white cold and threatening", 326 "promptEn": "woman in white cold and threatening",
309 - "lastPlayedAt": 1788232355821 327 + "lastPlayedAt": 1788236497319
310 }, 328 },
311 { 329 {
312 "id": "a46f1daa9ee0", 330 "id": "a46f1daa9ee0",
@@ -321,7 +339,7 @@ @@ -321,7 +339,7 @@
321 "likes": 0, 339 "likes": 0,
322 "createdAt": 1788181569491, 340 "createdAt": 1788181569491,
323 "promptEn": "a massive warship glides through the stars, the bridge suddenly shakes hard", 341 "promptEn": "a massive warship glides through the stars, the bridge suddenly shakes hard",
324 - "lastPlayedAt": 1788233312967 342 + "lastPlayedAt": 1788239135388
325 }, 343 },
326 { 344 {
327 "id": "44bf5b969e94", 345 "id": "44bf5b969e94",
@@ -336,7 +354,7 @@ @@ -336,7 +354,7 @@
336 "likes": 0, 354 "likes": 0,
337 "createdAt": 1788162094002, 355 "createdAt": 1788162094002,
338 "promptEn": "woman in old chinese robe talking, gentle but wary, in an old room", 356 "promptEn": "woman in old chinese robe talking, gentle but wary, in an old room",
339 - "lastPlayedAt": 1788231002876 357 + "lastPlayedAt": 1788238847245
340 }, 358 },
341 { 359 {
342 "id": "e17cfe57f56a", 360 "id": "e17cfe57f56a",
@@ -351,7 +369,7 @@ @@ -351,7 +369,7 @@
351 "likes": 0, 369 "likes": 0,
352 "createdAt": 1788160917846, 370 "createdAt": 1788160917846,
353 "promptEn": "woman in old chinese robe talking, gentle but wary, in an old room", 371 "promptEn": "woman in old chinese robe talking, gentle but wary, in an old room",
354 - "lastPlayedAt": 1788232031703 372 + "lastPlayedAt": 1788234265405
355 }, 373 },
356 { 374 {
357 "id": "24e49e5ae069", 375 "id": "24e49e5ae069",
@@ -381,7 +399,7 @@ @@ -381,7 +399,7 @@
381 "likes": 0, 399 "likes": 0,
382 "createdAt": 1788157322611, 400 "createdAt": 1788157322611,
383 "promptEn": "constable talking, looking scared", 401 "promptEn": "constable talking, looking scared",
384 - "lastPlayedAt": 1788230591198 402 + "lastPlayedAt": 1788239195414
385 }, 403 },
386 { 404 {
387 "id": "7977eeeabe8b", 405 "id": "7977eeeabe8b",
@@ -396,7 +414,7 @@ @@ -396,7 +414,7 @@
396 "likes": 0, 414 "likes": 0,
397 "createdAt": 1788157177505, 415 "createdAt": 1788157177505,
398 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen", 416 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen",
399 - "lastPlayedAt": 1788232081727 417 + "lastPlayedAt": 1788236561376
400 }, 418 },
401 { 419 {
402 "id": "a9c4ba5e453b", 420 "id": "a9c4ba5e453b",
@@ -411,7 +429,7 @@ @@ -411,7 +429,7 @@
411 "likes": 0, 429 "likes": 0,
412 "createdAt": 1788156878022, 430 "createdAt": 1788156878022,
413 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen", 431 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen",
414 - "lastPlayedAt": 1788233751168 432 + "lastPlayedAt": 1788238326802
415 }, 433 },
416 { 434 {
417 "id": "a0b752e7ac0d", 435 "id": "a0b752e7ac0d",
@@ -426,7 +444,7 @@ @@ -426,7 +444,7 @@
426 "likes": 0, 444 "likes": 0,
427 "createdAt": 1788154942596, 445 "createdAt": 1788154942596,
428 "promptEn": "constable talking, looking scared", 446 "promptEn": "constable talking, looking scared",
429 - "lastPlayedAt": 1788231156952 447 + "lastPlayedAt": 1788239059343
430 }, 448 },
431 { 449 {
432 "id": "b6608e2eac60", 450 "id": "b6608e2eac60",
@@ -441,7 +459,7 @@ @@ -441,7 +459,7 @@
441 "likes": 0, 459 "likes": 0,
442 "createdAt": 1788153988474, 460 "createdAt": 1788153988474,
443 "promptEn": "astronaut at a martian canyon, visor reflecting alien ruins, then aerial view of the city", 461 "promptEn": "astronaut at a martian canyon, visor reflecting alien ruins, then aerial view of the city",
444 - "lastPlayedAt": 1788232531824 462 + "lastPlayedAt": 1788238420888
445 }, 463 },
446 { 464 {
447 "id": "194fc2b56cb5", 465 "id": "194fc2b56cb5",
@@ -456,7 +474,7 @@ @@ -456,7 +474,7 @@
456 "likes": 0, 474 "likes": 0,
457 "createdAt": 1788138492364, 475 "createdAt": 1788138492364,
458 "promptEn": "astronaut on a Martian canyon, helmet reflecting an alien ruined city", 476 "promptEn": "astronaut on a Martian canyon, helmet reflecting an alien ruined city",
459 - "lastPlayedAt": 1788232051710 477 + "lastPlayedAt": 1788239857611
460 }, 478 },
461 { 479 {
462 "id": "f84b8f044ec5", 480 "id": "f84b8f044ec5",
@@ -486,7 +504,7 @@ @@ -486,7 +504,7 @@
486 "likes": 0, 504 "likes": 0,
487 "createdAt": 1788120171608, 505 "createdAt": 1788120171608,
488 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen", 506 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen",
489 - "lastPlayedAt": 1788232690632 507 + "lastPlayedAt": 1788239624540
490 }, 508 },
491 { 509 {
492 "id": "b8c762a3672e", 510 "id": "b8c762a3672e",
@@ -501,7 +519,7 @@ @@ -501,7 +519,7 @@
501 "likes": 0, 519 "likes": 0,
502 "createdAt": 1788106707982, 520 "createdAt": 1788106707982,
503 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen", 521 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen",
504 - "lastPlayedAt": 1788230489157 522 + "lastPlayedAt": 1788235032035
505 }, 523 },
506 { 524 {
507 "id": "1fa40bca47f5", 525 "id": "1fa40bca47f5",
@@ -516,7 +534,7 @@ @@ -516,7 +534,7 @@
516 "likes": 0, 534 "likes": 0,
517 "createdAt": 1788085282201, 535 "createdAt": 1788085282201,
518 "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen", 536 "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen",
519 - "lastPlayedAt": 1788230988868 537 + "lastPlayedAt": 1788238925278
520 }, 538 },
521 { 539 {
522 "id": "683ab2fd4681", 540 "id": "683ab2fd4681",
@@ -531,7 +549,7 @@ @@ -531,7 +549,7 @@
531 "likes": 0, 549 "likes": 0,
532 "createdAt": 1788071067702, 550 "createdAt": 1788071067702,
533 "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen", 551 "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen",
534 - "lastPlayedAt": 1788231799507 552 + "lastPlayedAt": 1788239269456
535 }, 553 },
536 { 554 {
537 "id": "2e09fcd3d42c", 555 "id": "2e09fcd3d42c",
@@ -546,7 +564,7 @@ @@ -546,7 +564,7 @@
546 "likes": 0, 564 "likes": 0,
547 "createdAt": 1788062322252, 565 "createdAt": 1788062322252,
548 "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen", 566 "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen",
549 - "lastPlayedAt": 1788233302967 567 + "lastPlayedAt": 1788237998513
550 }, 568 },
551 { 569 {
552 "id": "12ea9ce00f36", 570 "id": "12ea9ce00f36",
@@ -561,7 +579,7 @@ @@ -561,7 +579,7 @@
561 "likes": 0, 579 "likes": 0,
562 "createdAt": 1788039783070, 580 "createdAt": 1788039783070,
563 "promptEn": "silver-haired idol backstage, red light pulsing, offering a blood-red contract", 581 "promptEn": "silver-haired idol backstage, red light pulsing, offering a blood-red contract",
564 - "lastPlayedAt": 1788229466221 582 + "lastPlayedAt": 1788236086962
565 }, 583 },
566 { 584 {
567 "id": "75bdbacd3012", 585 "id": "75bdbacd3012",
@@ -576,7 +594,7 @@ @@ -576,7 +594,7 @@
576 "likes": 0, 594 "likes": 0,
577 "createdAt": 1788039782992, 595 "createdAt": 1788039782992,
578 "promptEn": "silver-haired idol backstage, red light pulsing, offering a blood-red contract", 596 "promptEn": "silver-haired idol backstage, red light pulsing, offering a blood-red contract",
579 - "lastPlayedAt": 1788230543177 597 + "lastPlayedAt": 1788238901269
580 }, 598 },
581 { 599 {
582 "id": "69e75a6b3997", 600 "id": "69e75a6b3997",
@@ -591,7 +609,7 @@ @@ -591,7 +609,7 @@
591 "likes": 0, 609 "likes": 0,
592 "createdAt": 1788027833836, 610 "createdAt": 1788027833836,
593 "promptEn": "wounded hunter at a ruined subway dungeon entrance, lightning flickers, pointing into the tunnel", 611 "promptEn": "wounded hunter at a ruined subway dungeon entrance, lightning flickers, pointing into the tunnel",
594 - "lastPlayedAt": 1788232085728 612 + "lastPlayedAt": 1788238448910
595 }, 613 },
596 { 614 {
597 "id": "cde399c27dc4", 615 "id": "cde399c27dc4",
@@ -606,7 +624,7 @@ @@ -606,7 +624,7 @@
606 "likes": 0, 624 "likes": 0,
607 "createdAt": 1788026877547, 625 "createdAt": 1788026877547,
608 "promptEn": "a deity in gold crown and red veil, quietly judging you in a moonlit bridal chamber", 626 "promptEn": "a deity in gold crown and red veil, quietly judging you in a moonlit bridal chamber",
609 - "lastPlayedAt": 1788229302095 627 + "lastPlayedAt": 1788239662572
610 }, 628 },
611 { 629 {
612 "id": "d5f811737e95", 630 "id": "d5f811737e95",
@@ -621,7 +639,7 @@ @@ -621,7 +639,7 @@
621 "likes": 0, 639 "likes": 0,
622 "createdAt": 1788026876886, 640 "createdAt": 1788026876886,
623 "promptEn": "apartment manager in an eerie corridor, red elevator light flickering, holding out a key", 641 "promptEn": "apartment manager in an eerie corridor, red elevator light flickering, holding out a key",
624 - "lastPlayedAt": 1788232386408 642 + "lastPlayedAt": 1788239031335
625 }, 643 },
626 { 644 {
627 "id": "cdd03cca8461", 645 "id": "cdd03cca8461",
@@ -636,7 +654,7 @@ @@ -636,7 +654,7 @@
636 "likes": 0, 654 "likes": 0,
637 "createdAt": 1788026569422, 655 "createdAt": 1788026569422,
638 "promptEn": "stitched-face long-haired curse on a subway platform, flickering lights, cruel smile", 656 "promptEn": "stitched-face long-haired curse on a subway platform, flickering lights, cruel smile",
639 - "lastPlayedAt": 1788233199827 657 + "lastPlayedAt": 1788239131385
640 }, 658 },
641 { 659 {
642 "id": "b8dd0f62f648", 660 "id": "b8dd0f62f648",
@@ -651,7 +669,7 @@ @@ -651,7 +669,7 @@
651 "likes": 0, 669 "likes": 0,
652 "createdAt": 1788026569123, 670 "createdAt": 1788026569123,
653 "promptEn": "pale dream demon in a suit, reaching out a hand inside a dream train car", 671 "promptEn": "pale dream demon in a suit, reaching out a hand inside a dream train car",
654 - "lastPlayedAt": 1788232663823 672 + "lastPlayedAt": 1788237442156
655 }, 673 },
656 { 674 {
657 "id": "bd6e2b264243", 675 "id": "bd6e2b264243",
@@ -666,7 +684,7 @@ @@ -666,7 +684,7 @@
666 "likes": 0, 684 "likes": 0,
667 "createdAt": 1788026568815, 685 "createdAt": 1788026568815,
668 "promptEn": "blond soldier in combat gear, bracing at the entrance of a ruined basement battlefield", 686 "promptEn": "blond soldier in combat gear, bracing at the entrance of a ruined basement battlefield",
669 - "lastPlayedAt": 1788233058865 687 + "lastPlayedAt": 1788237670284
670 }, 688 },
671 { 689 {
672 "id": "b0d6af33eace", 690 "id": "b0d6af33eace",
@@ -681,7 +699,7 @@ @@ -681,7 +699,7 @@
681 "likes": 0, 699 "likes": 0,
682 "createdAt": 1788026568516, 700 "createdAt": 1788026568516,
683 "promptEn": "orange-haired villain in a black cloak with red clouds, raising a hand in a ruined village", 701 "promptEn": "orange-haired villain in a black cloak with red clouds, raising a hand in a ruined village",
684 - "lastPlayedAt": 1788233126898 702 + "lastPlayedAt": 1788239243436
685 }, 703 },
686 { 704 {
687 "id": "b077fd090415", 705 "id": "b077fd090415",
@@ -696,7 +714,7 @@ @@ -696,7 +714,7 @@
696 "likes": 0, 714 "likes": 0,
697 "createdAt": 1788026568242, 715 "createdAt": 1788026568242,
698 "promptEn": "magma commander in a white coat, raising a lava fist on the battlefield", 716 "promptEn": "magma commander in a white coat, raising a lava fist on the battlefield",
699 - "lastPlayedAt": 1788232916802 717 + "lastPlayedAt": 1788239786023
700 }, 718 },
701 { 719 {
702 "id": "1442013e94f0", 720 "id": "1442013e94f0",
@@ -711,7 +729,7 @@ @@ -711,7 +729,7 @@
711 "likes": 0, 729 "likes": 0,
712 "createdAt": 1788026068497, 730 "createdAt": 1788026068497,
713 "promptEn": "black-haired prince in a red cloak, pressing down an execution order in the trial hall", 731 "promptEn": "black-haired prince in a red cloak, pressing down an execution order in the trial hall",
714 - "lastPlayedAt": 1788232404430 732 + "lastPlayedAt": 1788235548506
715 }, 733 },
716 { 734 {
717 "id": "0a5e37bb328c", 735 "id": "0a5e37bb328c",
@@ -726,7 +744,7 @@ @@ -726,7 +744,7 @@
726 "likes": 0, 744 "likes": 0,
727 "createdAt": 1788025642732, 745 "createdAt": 1788025642732,
728 "promptEn": "Vecna reaching out a claw in his lair in the Upside Down, red lightning", 746 "promptEn": "Vecna reaching out a claw in his lair in the Upside Down, red lightning",
729 - "lastPlayedAt": 1788232907826 747 + "lastPlayedAt": 1788236459278
730 }, 748 },
731 { 749 {
732 "id": "00862f966c3f", 750 "id": "00862f966c3f",
@@ -741,7 +759,7 @@ @@ -741,7 +759,7 @@
741 "likes": 0, 759 "likes": 0,
742 "createdAt": 1788025640972, 760 "createdAt": 1788025640972,
743 "promptEn": "Voldemort in a graveyard, wand raised, stepping closer", 761 "promptEn": "Voldemort in a graveyard, wand raised, stepping closer",
744 - "lastPlayedAt": 1788232551825 762 + "lastPlayedAt": 1788233989281
745 }, 763 },
746 { 764 {
747 "id": "c3875fbd6cc4", 765 "id": "c3875fbd6cc4",
@@ -756,7 +774,7 @@ @@ -756,7 +774,7 @@
756 "likes": 0, 774 "likes": 0,
757 "createdAt": 1788025632028, 775 "createdAt": 1788025632028,
758 "promptEn": "a giant mouth embedded in the wall at the end of an endless hotel corridor, slowly opening", 776 "promptEn": "a giant mouth embedded in the wall at the end of an endless hotel corridor, slowly opening",
759 - "lastPlayedAt": 1788231351046 777 + "lastPlayedAt": 1788239439551
760 }, 778 },
761 { 779 {
762 "id": "1c6e2df243f4", 780 "id": "1c6e2df243f4",
@@ -771,7 +789,7 @@ @@ -771,7 +789,7 @@
771 "likes": 0, 789 "likes": 0,
772 "createdAt": 1788025630249, 790 "createdAt": 1788025630249,
773 "promptEn": "white-haired villain in a ruined street at night, cracked smile, reaching out a hand", 791 "promptEn": "white-haired villain in a ruined street at night, cracked smile, reaching out a hand",
774 - "lastPlayedAt": 1788229732479 792 + "lastPlayedAt": 1788237604251
775 }, 793 },
776 { 794 {
777 "id": "d735f1fc010f", 795 "id": "d735f1fc010f",
@@ -786,7 +804,7 @@ @@ -786,7 +804,7 @@
786 "likes": 0, 804 "likes": 0,
787 "createdAt": 1788025624377, 805 "createdAt": 1788025624377,
788 "promptEn": "cat-eared white-haired guard crouching in a ruined palace, purple aura around them", 806 "promptEn": "cat-eared white-haired guard crouching in a ruined palace, purple aura around them",
789 - "lastPlayedAt": 1788232950817 807 + "lastPlayedAt": 1788239069350
790 }, 808 },
791 { 809 {
792 "id": "b16b64e52f83", 810 "id": "b16b64e52f83",
@@ -801,7 +819,7 @@ @@ -801,7 +819,7 @@
801 "likes": 0, 819 "likes": 0,
802 "createdAt": 1788025623833, 820 "createdAt": 1788025623833,
803 "promptEn": "cat-eared white-haired guard crouching in a ruined palace, purple aura around them", 821 "promptEn": "cat-eared white-haired guard crouching in a ruined palace, purple aura around them",
804 - "lastPlayedAt": 1788231919621 822 + "lastPlayedAt": 1788237726312
805 }, 823 },
806 { 824 {
807 "id": "0d721acbc298", 825 "id": "0d721acbc298",
@@ -816,7 +834,7 @@ @@ -816,7 +834,7 @@
816 "likes": 0, 834 "likes": 0,
817 "createdAt": 1788025622363, 835 "createdAt": 1788025622363,
818 "promptEn": "detective in a surveillance room squinting at the monitors", 836 "promptEn": "detective in a surveillance room squinting at the monitors",
819 - "lastPlayedAt": 1788228717592 837 + "lastPlayedAt": 1788237418135
820 }, 838 },
821 { 839 {
822 "id": "639069dea7c5", 840 "id": "639069dea7c5",
@@ -831,7 +849,7 @@ @@ -831,7 +849,7 @@
831 "likes": 0, 849 "likes": 0,
832 "createdAt": 1788025620717, 850 "createdAt": 1788025620717,
833 "promptEn": "alien tyrant holding a glowing energy sphere, sneering on a rocky green battlefield", 851 "promptEn": "alien tyrant holding a glowing energy sphere, sneering on a rocky green battlefield",
834 - "lastPlayedAt": 1788230286981 852 + "lastPlayedAt": 1788238961298
835 }, 853 },
836 { 854 {
837 "id": "9db41d807330", 855 "id": "9db41d807330",
@@ -846,7 +864,7 @@ @@ -846,7 +864,7 @@
846 "likes": 0, 864 "likes": 0,
847 "createdAt": 1788025220954, 865 "createdAt": 1788025220954,
848 "promptEn": "pirate captain plants a cutlass on the table, making a dangerous bargain", 866 "promptEn": "pirate captain plants a cutlass on the table, making a dangerous bargain",
849 - "lastPlayedAt": 1788232503823 867 + "lastPlayedAt": 1788239620537
850 }, 868 },
851 { 869 {
852 "id": "3e03975ff40a", 870 "id": "3e03975ff40a",
@@ -861,7 +879,7 @@ @@ -861,7 +879,7 @@
861 "likes": 0, 879 "likes": 0,
862 "createdAt": 1788025003469, 880 "createdAt": 1788025003469,
863 "promptEn": "vampire idol with red eyes, offering a blood-red contract backstage", 881 "promptEn": "vampire idol with red eyes, offering a blood-red contract backstage",
864 - "lastPlayedAt": 1788230557183 882 + "lastPlayedAt": 1788238038549
865 }, 883 },
866 { 884 {
867 "id": "2857aacf11bb", 885 "id": "2857aacf11bb",
@@ -876,7 +894,7 @@ @@ -876,7 +894,7 @@
876 "likes": 0, 894 "likes": 0,
877 "createdAt": 1788024900966, 895 "createdAt": 1788024900966,
878 "promptEn": "player 101 blocking the path on a high glass bridge, threatening you", 896 "promptEn": "player 101 blocking the path on a high glass bridge, threatening you",
879 - "lastPlayedAt": 1788229306100 897 + "lastPlayedAt": 1788238777192
880 }, 898 },
881 { 899 {
882 "id": "d6aab3fce63d", 900 "id": "d6aab3fce63d",
@@ -891,7 +909,7 @@ @@ -891,7 +909,7 @@
891 "likes": 0, 909 "likes": 0,
892 "createdAt": 1788024747209, 910 "createdAt": 1788024747209,
893 "promptEn": "wounded hunter leaning on a subway turnstile, ordering you into the tunnel", 911 "promptEn": "wounded hunter leaning on a subway turnstile, ordering you into the tunnel",
894 - "lastPlayedAt": 1788229298090 912 + "lastPlayedAt": 1788239325490
895 }, 913 },
896 { 914 {
897 "id": "fdefca4e4980", 915 "id": "fdefca4e4980",
@@ -906,7 +924,7 @@ @@ -906,7 +924,7 @@
906 "likes": 0, 924 "likes": 0,
907 "createdAt": 1788024491625, 925 "createdAt": 1788024491625,
908 "promptEn": "prince signs an execution order in the trial hall, points at the guillotine", 926 "promptEn": "prince signs an execution order in the trial hall, points at the guillotine",
909 - "lastPlayedAt": 1788230830800 927 + "lastPlayedAt": 1788239261453
910 }, 928 },
911 { 929 {
912 "id": "8a933feb52c4", 930 "id": "8a933feb52c4",
@@ -921,7 +939,7 @@ @@ -921,7 +939,7 @@
921 "likes": 0, 939 "likes": 0,
922 "createdAt": 1788019483999, 940 "createdAt": 1788019483999,
923 "promptEn": "constable rushes in, glances back at the door, then talks to the woman behind the screen", 941 "promptEn": "constable rushes in, glances back at the door, then talks to the woman behind the screen",
924 - "lastPlayedAt": 1788233350990 942 + "lastPlayedAt": 1788238909272
925 }, 943 },
926 { 944 {
927 "id": "998e3e6f9a2f", 945 "id": "998e3e6f9a2f",
@@ -936,7 +954,7 @@ @@ -936,7 +954,7 @@
936 "likes": 0, 954 "likes": 0,
937 "createdAt": 1788016668421, 955 "createdAt": 1788016668421,
938 "promptEn": "pirate negotiation, catching Barbossa's flaw and turning the tables", 956 "promptEn": "pirate negotiation, catching Barbossa's flaw and turning the tables",
939 - "lastPlayedAt": 1788233635107 957 + "lastPlayedAt": 1788238619060
940 }, 958 },
941 { 959 {
942 "id": "e3b18b5dfd8e", 960 "id": "e3b18b5dfd8e",
@@ -951,7 +969,7 @@ @@ -951,7 +969,7 @@
951 "likes": 0, 969 "likes": 0,
952 "createdAt": 1788016434846, 970 "createdAt": 1788016434846,
953 "promptEn": "pirate negotiation, catching Barbossa's flaw and turning the tables", 971 "promptEn": "pirate negotiation, catching Barbossa's flaw and turning the tables",
954 - "lastPlayedAt": 1788230098805 972 + "lastPlayedAt": 1788237756332
955 }, 973 },
956 { 974 {
957 "id": "03e32ff5f0ef", 975 "id": "03e32ff5f0ef",
@@ -966,7 +984,7 @@ @@ -966,7 +984,7 @@
966 "likes": 0, 984 "likes": 0,
967 "createdAt": 1788016203093, 985 "createdAt": 1788016203093,
968 "promptEn": "pirate negotiation, held the goal but paid a price", 986 "promptEn": "pirate negotiation, held the goal but paid a price",
969 - "lastPlayedAt": 1788230908828 987 + "lastPlayedAt": 1788236333170
970 }, 988 },
971 { 989 {
972 "id": "18aa882cbd0b", 990 "id": "18aa882cbd0b",
@@ -981,7 +999,7 @@ @@ -981,7 +999,7 @@
981 "likes": 0, 999 "likes": 0,
982 "createdAt": 1788015968838, 1000 "createdAt": 1788015968838,
983 "promptEn": "pirate negotiation, held the goal but paid a price", 1001 "promptEn": "pirate negotiation, held the goal but paid a price",
984 - "lastPlayedAt": 1788233671127 1002 + "lastPlayedAt": 1788238100608
985 }, 1003 },
986 { 1004 {
987 "id": "b328649000d1", 1005 "id": "b328649000d1",
@@ -996,7 +1014,7 @@ @@ -996,7 +1014,7 @@
996 "likes": 0, 1014 "likes": 0,
997 "createdAt": 1788015737873, 1015 "createdAt": 1788015737873,
998 "promptEn": "pirate negotiation, went in head-on and Barbossa saw it coming", 1016 "promptEn": "pirate negotiation, went in head-on and Barbossa saw it coming",
999 - "lastPlayedAt": 1788231967667 1017 + "lastPlayedAt": 1788237117870
1000 }, 1018 },
1001 { 1019 {
1002 "id": "2a5ee87b8f13", 1020 "id": "2a5ee87b8f13",
@@ -1011,7 +1029,7 @@ @@ -1011,7 +1029,7 @@
1011 "likes": 0, 1029 "likes": 0,
1012 "createdAt": 1788015493445, 1030 "createdAt": 1788015493445,
1013 "promptEn": "pirate negotiation, went in head-on and Barbossa saw it coming", 1031 "promptEn": "pirate negotiation, went in head-on and Barbossa saw it coming",
1014 - "lastPlayedAt": 1788233308972 1032 + "lastPlayedAt": 1788237736321
1015 }, 1033 },
1016 { 1034 {
1017 "id": "3f30f5097438", 1035 "id": "3f30f5097438",
@@ -1026,7 +1044,7 @@ @@ -1026,7 +1044,7 @@
1026 "likes": 0, 1044 "likes": 0,
1027 "createdAt": 1788015035234, 1045 "createdAt": 1788015035234,
1028 "promptEn": "pirate negotiation, stayed passive and got cornered", 1046 "promptEn": "pirate negotiation, stayed passive and got cornered",
1029 - "lastPlayedAt": 1788229614350 1047 + "lastPlayedAt": 1788238905268
1030 }, 1048 },
1031 { 1049 {
1032 "id": "6a0d14cabe17", 1050 "id": "6a0d14cabe17",
@@ -1041,7 +1059,7 @@ @@ -1041,7 +1059,7 @@
1041 "likes": 0, 1059 "likes": 0,
1042 "createdAt": 1788014791733, 1060 "createdAt": 1788014791733,
1043 "promptEn": "pirate negotiation, stayed passive and got cornered", 1061 "promptEn": "pirate negotiation, stayed passive and got cornered",
1044 - "lastPlayedAt": 1788229432201 1062 + "lastPlayedAt": 1788239570515
1045 }, 1063 },
1046 { 1064 {
1047 "id": "08120928cc77", 1065 "id": "08120928cc77",
@@ -1056,7 +1074,7 @@ @@ -1056,7 +1074,7 @@
1056 "likes": 0, 1074 "likes": 0,
1057 "createdAt": 1788014790969, 1075 "createdAt": 1788014790969,
1058 "promptEn": "pirate negotiation, stayed passive and got cornered", 1076 "promptEn": "pirate negotiation, stayed passive and got cornered",
1059 - "lastPlayedAt": 1788232946815 1077 + "lastPlayedAt": 1788239443550
1060 }, 1078 },
1061 { 1079 {
1062 "id": "e14fe7c3220e", 1080 "id": "e14fe7c3220e",
@@ -1071,7 +1089,7 @@ @@ -1071,7 +1089,7 @@
1071 "likes": 0, 1089 "likes": 0,
1072 "createdAt": 1788014563344, 1090 "createdAt": 1788014563344,
1073 "promptEn": "captain's cabin on the Black Pearl, surrounded by pirates, negotiation could turn into an execution", 1091 "promptEn": "captain's cabin on the Black Pearl, surrounded by pirates, negotiation could turn into an execution",
1074 - "lastPlayedAt": 1788231511189 1092 + "lastPlayedAt": 1788238589036
1075 }, 1093 },
1076 { 1094 {
1077 "id": "c6e8560f62a2", 1095 "id": "c6e8560f62a2",
@@ -1086,7 +1104,7 @@ @@ -1086,7 +1104,7 @@
1086 "likes": 0, 1104 "likes": 0,
1087 "createdAt": 1788014336515, 1105 "createdAt": 1788014336515,
1088 "promptEn": "captain's cabin on the Black Pearl, surrounded by pirates, negotiation could turn into an execution", 1106 "promptEn": "captain's cabin on the Black Pearl, surrounded by pirates, negotiation could turn into an execution",
1089 - "lastPlayedAt": 1788232214325 1107 + "lastPlayedAt": 1788235820732
1090 }, 1108 },
1091 { 1109 {
1092 "id": "eae105ee1204", 1110 "id": "eae105ee1204",
@@ -1101,7 +1119,7 @@ @@ -1101,7 +1119,7 @@
1101 "likes": 0, 1119 "likes": 0,
1102 "createdAt": 1788014099497, 1120 "createdAt": 1788014099497,
1103 "promptEn": "glass bridge game, exploiting a flaw to push back player 101", 1121 "promptEn": "glass bridge game, exploiting a flaw to push back player 101",
1104 - "lastPlayedAt": 1788230116823 1122 + "lastPlayedAt": 1788237704301
1105 }, 1123 },
1106 { 1124 {
1107 "id": "24576c264a25", 1125 "id": "24576c264a25",
@@ -1116,7 +1134,7 @@ @@ -1116,7 +1134,7 @@
1116 "likes": 0, 1134 "likes": 0,
1117 "createdAt": 1788013870849, 1135 "createdAt": 1788013870849,
1118 "promptEn": "glass bridge game, exploiting a flaw to push back player 101", 1136 "promptEn": "glass bridge game, exploiting a flaw to push back player 101",
1119 - "lastPlayedAt": 1788229452214 1137 + "lastPlayedAt": 1788237432150
1120 }, 1138 },
1121 { 1139 {
1122 "id": "dd12bbb55060", 1140 "id": "dd12bbb55060",
@@ -1131,7 +1149,7 @@ @@ -1131,7 +1149,7 @@
1131 "likes": 0, 1149 "likes": 0,
1132 "createdAt": 1788013634333, 1150 "createdAt": 1788013634333,
1133 "promptEn": "glass bridge game, held the goal but paid a price", 1151 "promptEn": "glass bridge game, held the goal but paid a price",
1134 - "lastPlayedAt": 1788232940812 1152 + "lastPlayedAt": 1788238096601
1135 }, 1153 },
1136 { 1154 {
1137 "id": "5b8ef21616d4", 1155 "id": "5b8ef21616d4",
@@ -1146,7 +1164,7 @@ @@ -1146,7 +1164,7 @@
1146 "likes": 0, 1164 "likes": 0,
1147 "createdAt": 1788013632065, 1165 "createdAt": 1788013632065,
1148 "promptEn": "glass bridge game, held the goal but paid a price", 1166 "promptEn": "glass bridge game, held the goal but paid a price",
1149 - "lastPlayedAt": 1788232107744 1167 + "lastPlayedAt": 1788238787200
1150 }, 1168 },
1151 { 1169 {
1152 "id": "892b923eed29", 1170 "id": "892b923eed29",
@@ -1161,7 +1179,7 @@ @@ -1161,7 +1179,7 @@
1161 "likes": 0, 1179 "likes": 0,
1162 "createdAt": 1788013393463, 1180 "createdAt": 1788013393463,
1163 "promptEn": "guessed wrong in a survival game, got countered", 1181 "promptEn": "guessed wrong in a survival game, got countered",
1164 - "lastPlayedAt": 1788233653115 1182 + "lastPlayedAt": 1788239548495
1165 }, 1183 },
1166 { 1184 {
1167 "id": "7880a91928c6", 1185 "id": "7880a91928c6",
@@ -1176,7 +1194,7 @@ @@ -1176,7 +1194,7 @@
1176 "likes": 0, 1194 "likes": 0,
1177 "createdAt": 1788013155592, 1195 "createdAt": 1788013155592,
1178 "promptEn": "glass bridge game, stayed passive and got cornered", 1196 "promptEn": "glass bridge game, stayed passive and got cornered",
1179 - "lastPlayedAt": 1788230447128 1197 + "lastPlayedAt": 1788237956491
1180 }, 1198 },
1181 { 1199 {
1182 "id": "f80c8ec1a8c8", 1200 "id": "f80c8ec1a8c8",
@@ -1191,7 +1209,7 @@ @@ -1191,7 +1209,7 @@
1191 "likes": 0, 1209 "likes": 0,
1192 "createdAt": 1788012917294, 1210 "createdAt": 1788012917294,
1193 "promptEn": "standing on the glass bridge, facing off with player 101", 1211 "promptEn": "standing on the glass bridge, facing off with player 101",
1194 - "lastPlayedAt": 1788233130898 1212 + "lastPlayedAt": 1788237284002
1195 }, 1213 },
1196 { 1214 {
1197 "id": "8f23de61904f", 1215 "id": "8f23de61904f",
@@ -1206,7 +1224,7 @@ @@ -1206,7 +1224,7 @@
1206 "likes": 0, 1224 "likes": 0,
1207 "createdAt": 1788012917185, 1225 "createdAt": 1788012917185,
1208 "promptEn": "standing on the glass bridge, facing off with player 101", 1226 "promptEn": "standing on the glass bridge, facing off with player 101",
1209 - "lastPlayedAt": 1788233296966 1227 + "lastPlayedAt": 1788239642565
1210 }, 1228 },
1211 { 1229 {
1212 "id": "bb4a8351d822", 1230 "id": "bb4a8351d822",
@@ -1221,7 +1239,7 @@ @@ -1221,7 +1239,7 @@
1221 "likes": 0, 1239 "likes": 0,
1222 "createdAt": 1788012679740, 1240 "createdAt": 1788012679740,
1223 "promptEn": "in the mental lair, exploiting a flaw to counter Vecna", 1241 "promptEn": "in the mental lair, exploiting a flaw to counter Vecna",
1224 - "lastPlayedAt": 1788229618353 1242 + "lastPlayedAt": 1788236895678
1225 }, 1243 },
1226 { 1244 {
1227 "id": "3913752bbe3a", 1245 "id": "3913752bbe3a",
@@ -1236,7 +1254,7 @@ @@ -1236,7 +1254,7 @@
1236 "likes": 0, 1254 "likes": 0,
1237 "createdAt": 1788012441458, 1255 "createdAt": 1788012441458,
1238 "promptEn": "facing Vecna, held the goal but paid a price", 1256 "promptEn": "facing Vecna, held the goal but paid a price",
1239 - "lastPlayedAt": 1788232483825 1257 + "lastPlayedAt": 1788236173036
1240 }, 1258 },
1241 { 1259 {
1242 "id": "691c2f679759", 1260 "id": "691c2f679759",
@@ -1251,7 +1269,7 @@ @@ -1251,7 +1269,7 @@
1251 "likes": 0, 1269 "likes": 0,
1252 "createdAt": 1788012202802, 1270 "createdAt": 1788012202802,
1253 "promptEn": "facing Vecna, went in head-on and got seen through", 1271 "promptEn": "facing Vecna, went in head-on and got seen through",
1254 - "lastPlayedAt": 1788232320378 1272 + "lastPlayedAt": 1788235248233
1255 }, 1273 },
1256 { 1274 {
1257 "id": "07f957783a09", 1275 "id": "07f957783a09",
@@ -1266,7 +1284,7 @@ @@ -1266,7 +1284,7 @@
1266 "likes": 0, 1284 "likes": 0,
1267 "createdAt": 1788012202708, 1285 "createdAt": 1788012202708,
1268 "promptEn": "facing Vecna, went in head-on and got seen through", 1286 "promptEn": "facing Vecna, went in head-on and got seen through",
1269 - "lastPlayedAt": 1788232140287 1287 + "lastPlayedAt": 1788239507583
1270 }, 1288 },
1271 { 1289 {
1272 "id": "40ccb04f76e9", 1290 "id": "40ccb04f76e9",
@@ -1281,7 +1299,7 @@ @@ -1281,7 +1299,7 @@
1281 "likes": 0, 1299 "likes": 0,
1282 "createdAt": 1788011964828, 1300 "createdAt": 1788011964828,
1283 "promptEn": "facing Vecna, stayed passive and got cornered", 1301 "promptEn": "facing Vecna, stayed passive and got cornered",
1284 - "lastPlayedAt": 1788232971826 1302 + "lastPlayedAt": 1788235900804
1285 }, 1303 },
1286 { 1304 {
1287 "id": "7161aecafa32", 1305 "id": "7161aecafa32",
@@ -1296,7 +1314,7 @@ @@ -1296,7 +1314,7 @@
1296 "likes": 0, 1314 "likes": 0,
1297 "createdAt": 1788011727444, 1315 "createdAt": 1788011727444,
1298 "promptEn": "entering Vecna's mental lair, red lightning, twisted space", 1316 "promptEn": "entering Vecna's mental lair, red lightning, twisted space",
1299 - "lastPlayedAt": 1788232338391 1317 + "lastPlayedAt": 1788237488190
1300 }, 1318 },
1301 { 1319 {
1302 "id": "6a9af7e7368d", 1320 "id": "6a9af7e7368d",
@@ -1311,7 +1329,7 @@ @@ -1311,7 +1329,7 @@
1311 "likes": 0, 1329 "likes": 0,
1312 "createdAt": 1788011488775, 1330 "createdAt": 1788011488775,
1313 "promptEn": "graveyard standoff, exploiting a flaw to counter Voldemort", 1331 "promptEn": "graveyard standoff, exploiting a flaw to counter Voldemort",
1314 - "lastPlayedAt": 1788233272950 1332 + "lastPlayedAt": 1788237678291
1315 }, 1333 },
1316 { 1334 {
1317 "id": "24845be71ea0", 1335 "id": "24845be71ea0",
@@ -1326,7 +1344,7 @@ @@ -1326,7 +1344,7 @@
1326 "likes": 0, 1344 "likes": 0,
1327 "createdAt": 1788011488667, 1345 "createdAt": 1788011488667,
1328 "promptEn": "graveyard standoff, exploiting a flaw to counter Voldemort", 1346 "promptEn": "graveyard standoff, exploiting a flaw to counter Voldemort",
1329 - "lastPlayedAt": 1788231671384 1347 + "lastPlayedAt": 1788239425542
1330 }, 1348 },
1331 { 1349 {
1332 "id": "f1012a3d7aa7", 1350 "id": "f1012a3d7aa7",
@@ -1341,7 +1359,7 @@ @@ -1341,7 +1359,7 @@
1341 "likes": 0, 1359 "likes": 0,
1342 "createdAt": 1788011251158, 1360 "createdAt": 1788011251158,
1343 "promptEn": "facing Voldemort, held the goal but paid a price", 1361 "promptEn": "facing Voldemort, held the goal but paid a price",
1344 - "lastPlayedAt": 1788231773483 1362 + "lastPlayedAt": 1788239329493
1345 }, 1363 },
1346 { 1364 {
1347 "id": "58f68b111429", 1365 "id": "58f68b111429",
@@ -1356,7 +1374,7 @@ @@ -1356,7 +1374,7 @@
1356 "likes": 0, 1374 "likes": 0,
1357 "createdAt": 1788011013309, 1375 "createdAt": 1788011013309,
1358 "promptEn": "facing Voldemort, went in head-on and got seen through", 1376 "promptEn": "facing Voldemort, went in head-on and got seen through",
1359 - "lastPlayedAt": 1788231341043 1377 + "lastPlayedAt": 1788234335440
1360 }, 1378 },
1361 { 1379 {
1362 "id": "682cbc166479", 1380 "id": "682cbc166479",
@@ -1371,7 +1389,7 @@ @@ -1371,7 +1389,7 @@
1371 "likes": 0, 1389 "likes": 0,
1372 "createdAt": 1788010775613, 1390 "createdAt": 1788010775613,
1373 "promptEn": "facing Voldemort, stayed passive and got cornered", 1391 "promptEn": "facing Voldemort, stayed passive and got cornered",
1374 - "lastPlayedAt": 1788233344985 1392 + "lastPlayedAt": 1788239736647
1375 }, 1393 },
1376 { 1394 {
1377 "id": "795842efa4a1", 1395 "id": "795842efa4a1",
@@ -1386,7 +1404,7 @@ @@ -1386,7 +1404,7 @@
1386 "likes": 0, 1404 "likes": 0,
1387 "createdAt": 1788010537514, 1405 "createdAt": 1788010537514,
1388 "promptEn": "the triwizard cup teleports you to the graveyard, Voldemort's revival just finished", 1406 "promptEn": "the triwizard cup teleports you to the graveyard, Voldemort's revival just finished",
1389 - "lastPlayedAt": 1788233095826 1407 + "lastPlayedAt": 1788238659090
1390 }, 1408 },
1391 { 1409 {
1392 "id": "6ed9eaf4fa1d", 1410 "id": "6ed9eaf4fa1d",
@@ -1401,7 +1419,7 @@ @@ -1401,7 +1419,7 @@
1401 "likes": 0, 1419 "likes": 0,
1402 "createdAt": 1788010299688, 1420 "createdAt": 1788010299688,
1403 "promptEn": "facing Thanos, exploiting a flaw to counter him", 1421 "promptEn": "facing Thanos, exploiting a flaw to counter him",
1404 - "lastPlayedAt": 1788232151824 1422 + "lastPlayedAt": 1788237710304
1405 }, 1423 },
1406 { 1424 {
1407 "id": "469294a4ed09", 1425 "id": "469294a4ed09",
@@ -1416,7 +1434,7 @@ @@ -1416,7 +1434,7 @@
1416 "likes": 0, 1434 "likes": 0,
1417 "createdAt": 1788010062535, 1435 "createdAt": 1788010062535,
1418 "promptEn": "facing Thanos, held the goal but paid a price", 1436 "promptEn": "facing Thanos, held the goal but paid a price",
1419 - "lastPlayedAt": 1788233166915 1437 + "lastPlayedAt": 1788235376337
1420 }, 1438 },
1421 { 1439 {
1422 "id": "324b84ebc607", 1440 "id": "324b84ebc607",
@@ -1431,7 +1449,7 @@ @@ -1431,7 +1449,7 @@
1431 "likes": 0, 1449 "likes": 0,
1432 "createdAt": 1788009825082, 1450 "createdAt": 1788009825082,
1433 "promptEn": "facing Thanos, went in head-on and got seen through", 1451 "promptEn": "facing Thanos, went in head-on and got seen through",
1434 - "lastPlayedAt": 1788233391003 1452 + "lastPlayedAt": 1788238058572
1435 }, 1453 },
1436 { 1454 {
1437 "id": "a6ead2cbe03f", 1455 "id": "a6ead2cbe03f",
@@ -1446,7 +1464,7 @@ @@ -1446,7 +1464,7 @@
1446 "likes": 0, 1464 "likes": 0,
1447 "createdAt": 1788009586293, 1465 "createdAt": 1788009586293,
1448 "promptEn": "facing Thanos, stayed passive and got cornered", 1466 "promptEn": "facing Thanos, stayed passive and got cornered",
1449 - "lastPlayedAt": 1788233681128 1467 + "lastPlayedAt": 1788239503570
1450 }, 1468 },
1451 { 1469 {
1452 "id": "438c98cf4973", 1470 "id": "438c98cf4973",
@@ -1461,7 +1479,7 @@ @@ -1461,7 +1479,7 @@
1461 "likes": 0, 1479 "likes": 0,
1462 "createdAt": 1788009346020, 1480 "createdAt": 1788009346020,
1463 "promptEn": "battlefield on Titan, the last chance to stop Thanos", 1481 "promptEn": "battlefield on Titan, the last chance to stop Thanos",
1464 - "lastPlayedAt": 1788229456216 1482 + "lastPlayedAt": 1788239381524
1465 }, 1483 },
1466 { 1484 {
1467 "id": "66a96c6bc7cb", 1485 "id": "66a96c6bc7cb",
@@ -1491,7 +1509,7 @@ @@ -1491,7 +1509,7 @@
1491 "likes": 0, 1509 "likes": 0,
1492 "createdAt": 1788008870118, 1510 "createdAt": 1788008870118,
1493 "promptEn": "facing the Eternity Devil, held the goal but paid a price", 1511 "promptEn": "facing the Eternity Devil, held the goal but paid a price",
1494 - "lastPlayedAt": 1788232976830 1512 + "lastPlayedAt": 1788235496464
1495 }, 1513 },
1496 { 1514 {
1497 "id": "35833c2cabb9", 1515 "id": "35833c2cabb9",
@@ -1506,7 +1524,7 @@ @@ -1506,7 +1524,7 @@
1506 "likes": 0, 1524 "likes": 0,
1507 "createdAt": 1788008632603, 1525 "createdAt": 1788008632603,
1508 "promptEn": "facing the Eternity Devil, went in head-on and got seen through", 1526 "promptEn": "facing the Eternity Devil, went in head-on and got seen through",
1509 - "lastPlayedAt": 1788233795940 1527 + "lastPlayedAt": 1788239149394
1510 }, 1528 },
1511 { 1529 {
1512 "id": "361c80537652", 1530 "id": "361c80537652",
@@ -1521,7 +1539,7 @@ @@ -1521,7 +1539,7 @@
1521 "likes": 0, 1539 "likes": 0,
1522 "createdAt": 1788008394344, 1540 "createdAt": 1788008394344,
1523 "promptEn": "facing the Eternity Devil, stayed passive and got cornered", 1541 "promptEn": "facing the Eternity Devil, stayed passive and got cornered",
1524 - "lastPlayedAt": 1788228541431 1542 + "lastPlayedAt": 1788236225074
1525 }, 1543 },
1526 { 1544 {
1527 "id": "1a57de01bad0", 1545 "id": "1a57de01bad0",
@@ -1536,7 +1554,7 @@ @@ -1536,7 +1554,7 @@
1536 "likes": 0, 1554 "likes": 0,
1537 "createdAt": 1788008156307, 1555 "createdAt": 1788008156307,
1538 "promptEn": "hotel floor sealed by the Eternity Devil, an endless looping corridor", 1556 "promptEn": "hotel floor sealed by the Eternity Devil, an endless looping corridor",
1539 - "lastPlayedAt": 1788229258057 1557 + "lastPlayedAt": 1788238969299
1540 }, 1558 },
1541 { 1559 {
1542 "id": "cd3b6dda90ad", 1560 "id": "cd3b6dda90ad",
@@ -1551,7 +1569,7 @@ @@ -1551,7 +1569,7 @@
1551 "likes": 0, 1569 "likes": 0,
1552 "createdAt": 1788007915610, 1570 "createdAt": 1788007915610,
1553 "promptEn": "in the ruined city, exploiting a flaw to counter Tomura", 1571 "promptEn": "in the ruined city, exploiting a flaw to counter Tomura",
1554 - "lastPlayedAt": 1788233841207 1572 + "lastPlayedAt": 1788236513335
1555 }, 1573 },
1556 { 1574 {
1557 "id": "f67685cd6862", 1575 "id": "f67685cd6862",
@@ -1581,7 +1599,7 @@ @@ -1581,7 +1599,7 @@
1581 "likes": 0, 1599 "likes": 0,
1582 "createdAt": 1788007441156, 1600 "createdAt": 1788007441156,
1583 "promptEn": "battlefield in Jaku city, the city in ruins, heroes in crisis", 1601 "promptEn": "battlefield in Jaku city, the city in ruins, heroes in crisis",
1584 - "lastPlayedAt": 1788232272360 1602 + "lastPlayedAt": 1788237350069
1585 }, 1603 },
1586 { 1604 {
1587 "id": "68219d6b68c8", 1605 "id": "68219d6b68c8",
@@ -1596,7 +1614,7 @@ @@ -1596,7 +1614,7 @@
1596 "likes": 0, 1614 "likes": 0,
1597 "createdAt": 1788007201908, 1615 "createdAt": 1788007201908,
1598 "promptEn": "breaking into the palace, Neferpitou stalling to protect the king", 1616 "promptEn": "breaking into the palace, Neferpitou stalling to protect the king",
1599 - "lastPlayedAt": 1788230437117 1617 + "lastPlayedAt": 1788239281462
1600 }, 1618 },
1601 { 1619 {
1602 "id": "628026375795", 1620 "id": "628026375795",
@@ -1611,7 +1629,7 @@ @@ -1611,7 +1629,7 @@
1611 "likes": 0, 1629 "likes": 0,
1612 "createdAt": 1788006964122, 1630 "createdAt": 1788006964122,
1613 "promptEn": "grabbing an evidence flaw to counter L", 1631 "promptEn": "grabbing an evidence flaw to counter L",
1614 - "lastPlayedAt": 1788232774691 1632 + "lastPlayedAt": 1788236128993
1615 }, 1633 },
1616 { 1634 {
1617 "id": "0c6306b07f79", 1635 "id": "0c6306b07f79",
@@ -1626,7 +1644,7 @@ @@ -1626,7 +1644,7 @@
1626 "likes": 0, 1644 "likes": 0,
1627 "createdAt": 1788006725887, 1645 "createdAt": 1788006725887,
1628 "promptEn": "facing off with L, went in head-on and got seen through", 1646 "promptEn": "facing off with L, went in head-on and got seen through",
1629 - "lastPlayedAt": 1788233326977 1647 + "lastPlayedAt": 1788238599043
1630 }, 1648 },
1631 { 1649 {
1632 "id": "ee82188227fa", 1650 "id": "ee82188227fa",
@@ -1641,7 +1659,7 @@ @@ -1641,7 +1659,7 @@
1641 "likes": 0, 1659 "likes": 0,
1642 "createdAt": 1788006252918, 1660 "createdAt": 1788006252918,
1643 "promptEn": "facing L, stayed passive and the evidence pressure maxed out", 1661 "promptEn": "facing L, stayed passive and the evidence pressure maxed out",
1644 - "lastPlayedAt": 1788233354985 1662 + "lastPlayedAt": 1788237536217
1645 }, 1663 },
1646 { 1664 {
1647 "id": "0e28a7fecf03", 1665 "id": "0e28a7fecf03",
@@ -1656,7 +1674,7 @@ @@ -1656,7 +1674,7 @@
1656 "likes": 0, 1674 "likes": 0,
1657 "createdAt": 1788006252824, 1675 "createdAt": 1788006252824,
1658 "promptEn": "facing L, stayed passive and the evidence pressure maxed out", 1676 "promptEn": "facing L, stayed passive and the evidence pressure maxed out",
1659 - "lastPlayedAt": 1788230964858 1677 + "lastPlayedAt": 1788238803216
1660 }, 1678 },
1661 { 1679 {
1662 "id": "fb0738ad3e3b", 1680 "id": "fb0738ad3e3b",
@@ -1671,7 +1689,7 @@ @@ -1671,7 +1689,7 @@
1671 "likes": 0, 1689 "likes": 0,
1672 "createdAt": 1788006015175, 1690 "createdAt": 1788006015175,
1673 "promptEn": "on the ruined planet, exploiting a flaw to counter Frieza", 1691 "promptEn": "on the ruined planet, exploiting a flaw to counter Frieza",
1674 - "lastPlayedAt": 1788233190924 1692 + "lastPlayedAt": 1788239487574
1675 }, 1693 },
1676 { 1694 {
1677 "id": "e6fda5c2237d", 1695 "id": "e6fda5c2237d",
@@ -1686,7 +1704,7 @@ @@ -1686,7 +1704,7 @@
1686 "likes": 0, 1704 "likes": 0,
1687 "createdAt": 1788005778588, 1705 "createdAt": 1788005778588,
1688 "promptEn": "facing Frieza, held the goal but paid a price", 1706 "promptEn": "facing Frieza, held the goal but paid a price",
1689 - "lastPlayedAt": 1788233701138 1707 + "lastPlayedAt": 1788239429548
1690 }, 1708 },
1691 { 1709 {
1692 "id": "b96cc9762cf6", 1710 "id": "b96cc9762cf6",
@@ -1716,7 +1734,7 @@ @@ -1716,7 +1734,7 @@
1716 "likes": 0, 1734 "likes": 0,
1717 "createdAt": 1788005305091, 1735 "createdAt": 1788005305091,
1718 "promptEn": "facing Frieza, stayed passive and got cornered", 1736 "promptEn": "facing Frieza, stayed passive and got cornered",
1719 - "lastPlayedAt": 1788232546520 1737 + "lastPlayedAt": 1788239513586
1720 }, 1738 },
1721 { 1739 {
1722 "id": "d680e111ade5", 1740 "id": "d680e111ade5",
@@ -1731,7 +1749,7 @@ @@ -1731,7 +1749,7 @@
1731 "likes": 0, 1749 "likes": 0,
1732 "createdAt": 1788005066484, 1750 "createdAt": 1788005066484,
1733 "promptEn": "Namek is about to be destroyed, the dragon ball wish still up for grabs", 1751 "promptEn": "Namek is about to be destroyed, the dragon ball wish still up for grabs",
1734 - "lastPlayedAt": 1788230088799 1752 + "lastPlayedAt": 1788236841630
1735 }, 1753 },
1736 { 1754 {
1737 "id": "565059763888", 1755 "id": "565059763888",
@@ -1746,7 +1764,7 @@ @@ -1746,7 +1764,7 @@
1746 "likes": 0, 1764 "likes": 0,
1747 "createdAt": 1788004828526, 1765 "createdAt": 1788004828526,
1748 "promptEn": "in the subway, exploiting a flaw to counter Mahito", 1766 "promptEn": "in the subway, exploiting a flaw to counter Mahito",
1749 - "lastPlayedAt": 1788226535712 1767 + "lastPlayedAt": 1788237532215
1750 }, 1768 },
1751 { 1769 {
1752 "id": "323737948733", 1770 "id": "323737948733",
@@ -1761,7 +1779,7 @@ @@ -1761,7 +1779,7 @@
1761 "likes": 0, 1779 "likes": 0,
1762 "createdAt": 1788004590937, 1780 "createdAt": 1788004590937,
1763 "promptEn": "facing Mahito, held the goal but paid a price", 1781 "promptEn": "facing Mahito, held the goal but paid a price",
1764 - "lastPlayedAt": 1788229662416 1782 + "lastPlayedAt": 1788239395530
1765 }, 1783 },
1766 { 1784 {
1767 "id": "9996a8ac8326", 1785 "id": "9996a8ac8326",
@@ -1776,7 +1794,7 @@ @@ -1776,7 +1794,7 @@
1776 "likes": 0, 1794 "likes": 0,
1777 "createdAt": 1788004353928, 1795 "createdAt": 1788004353928,
1778 "promptEn": "facing Mahito, went in head-on and got seen through", 1796 "promptEn": "facing Mahito, went in head-on and got seen through",
1779 - "lastPlayedAt": 1788232310379 1797 + "lastPlayedAt": 1788237410131
1780 }, 1798 },
1781 { 1799 {
1782 "id": "971d80ec087c", 1800 "id": "971d80ec087c",
@@ -1791,7 +1809,7 @@ @@ -1791,7 +1809,7 @@
1791 "likes": 0, 1809 "likes": 0,
1792 "createdAt": 1788004115725, 1810 "createdAt": 1788004115725,
1793 "promptEn": "facing Mahito, stayed passive and got cornered", 1811 "promptEn": "facing Mahito, stayed passive and got cornered",
1794 - "lastPlayedAt": 1788230311003 1812 + "lastPlayedAt": 1788239389526
1795 }, 1813 },
1796 { 1814 {
1797 "id": "5dd52c568f66", 1815 "id": "5dd52c568f66",
@@ -1806,7 +1824,7 @@ @@ -1806,7 +1824,7 @@
1806 "likes": 0, 1824 "likes": 0,
1807 "createdAt": 1788004115612, 1825 "createdAt": 1788004115612,
1808 "promptEn": "facing Mahito, stayed passive and got cornered", 1826 "promptEn": "facing Mahito, stayed passive and got cornered",
1809 - "lastPlayedAt": 1788232315826 1827 + "lastPlayedAt": 1788239333489
1810 }, 1828 },
1811 { 1829 {
1812 "id": "f4a2870386c6", 1830 "id": "f4a2870386c6",
@@ -1821,7 +1839,7 @@ @@ -1821,7 +1839,7 @@
1821 "likes": 0, 1839 "likes": 0,
1822 "createdAt": 1788003877425, 1840 "createdAt": 1788003877425,
1823 "promptEn": "in the dream, exploiting a flaw to counter Enmu", 1841 "promptEn": "in the dream, exploiting a flaw to counter Enmu",
1824 - "lastPlayedAt": 1788232025702 1842 + "lastPlayedAt": 1788239399534
1825 }, 1843 },
1826 { 1844 {
1827 "id": "7c4f9460e6b7", 1845 "id": "7c4f9460e6b7",
@@ -1836,7 +1854,7 @@ @@ -1836,7 +1854,7 @@
1836 "likes": 0, 1854 "likes": 0,
1837 "createdAt": 1788003639415, 1855 "createdAt": 1788003639415,
1838 "promptEn": "facing Enmu, held the goal but paid a price", 1856 "promptEn": "facing Enmu, held the goal but paid a price",
1839 - "lastPlayedAt": 1788229416189 1857 + "lastPlayedAt": 1788238813220
1840 }, 1858 },
1841 { 1859 {
1842 "id": "6881dbe839a0", 1860 "id": "6881dbe839a0",
@@ -1851,7 +1869,7 @@ @@ -1851,7 +1869,7 @@
1851 "likes": 0, 1869 "likes": 0,
1852 "createdAt": 1788003402559, 1870 "createdAt": 1788003402559,
1853 "promptEn": "facing Enmu, went in head-on and got seen through", 1871 "promptEn": "facing Enmu, went in head-on and got seen through",
1854 - "lastPlayedAt": 1788233214933 1872 + "lastPlayedAt": 1788239481564
1855 }, 1873 },
1856 { 1874 {
1857 "id": "5c0f1663f4ae", 1875 "id": "5c0f1663f4ae",
@@ -1866,7 +1884,7 @@ @@ -1866,7 +1884,7 @@
1866 "likes": 0, 1884 "likes": 0,
1867 "createdAt": 1788003166117, 1885 "createdAt": 1788003166117,
1868 "promptEn": "facing Enmu, stayed passive and got cornered", 1886 "promptEn": "facing Enmu, stayed passive and got cornered",
1869 - "lastPlayedAt": 1788232239825 1887 + "lastPlayedAt": 1788238218715
1870 }, 1888 },
1871 { 1889 {
1872 "id": "a3f39da74c1f", 1890 "id": "a3f39da74c1f",
@@ -1881,7 +1899,7 @@ @@ -1881,7 +1899,7 @@
1881 "likes": 0, 1899 "likes": 0,
1882 "createdAt": 1788002929455, 1900 "createdAt": 1788002929455,
1883 "promptEn": "facing Reiner, exploiting a flaw to counter him", 1901 "promptEn": "facing Reiner, exploiting a flaw to counter him",
1884 - "lastPlayedAt": 1788233194926 1902 + "lastPlayedAt": 1788239265453
1885 }, 1903 },
1886 { 1904 {
1887 "id": "ea8b38014674", 1905 "id": "ea8b38014674",
@@ -1896,7 +1914,7 @@ @@ -1896,7 +1914,7 @@
1896 "likes": 0, 1914 "likes": 0,
1897 "createdAt": 1788002691822, 1915 "createdAt": 1788002691822,
1898 "promptEn": "facing Reiner, held the goal but paid a price", 1916 "promptEn": "facing Reiner, held the goal but paid a price",
1899 - "lastPlayedAt": 1788232045709 1917 + "lastPlayedAt": 1788238975304
1900 }, 1918 },
1901 { 1919 {
1902 "id": "f65b15d67966", 1920 "id": "f65b15d67966",
@@ -1911,7 +1929,7 @@ @@ -1911,7 +1929,7 @@
1911 "likes": 0, 1929 "likes": 0,
1912 "createdAt": 1788002455066, 1930 "createdAt": 1788002455066,
1913 "promptEn": "facing Reiner, went in head-on and got seen through", 1931 "promptEn": "facing Reiner, went in head-on and got seen through",
1914 - "lastPlayedAt": 1788227954924 1932 + "lastPlayedAt": 1788237878416
1915 }, 1933 },
1916 { 1934 {
1917 "id": "5846d645e870", 1935 "id": "5846d645e870",
@@ -1926,7 +1944,7 @@ @@ -1926,7 +1944,7 @@
1926 "likes": 0, 1944 "likes": 0,
1927 "createdAt": 1788002217928, 1945 "createdAt": 1788002217928,
1928 "promptEn": "facing Reiner, stayed passive and got cornered", 1946 "promptEn": "facing Reiner, stayed passive and got cornered",
1929 - "lastPlayedAt": 1788229744490 1947 + "lastPlayedAt": 1788237580239
1930 }, 1948 },
1931 { 1949 {
1932 "id": "3fedf5531999", 1950 "id": "3fedf5531999",
@@ -1941,7 +1959,7 @@ @@ -1941,7 +1959,7 @@
1941 "likes": 0, 1959 "likes": 0,
1942 "createdAt": 1788001981272, 1960 "createdAt": 1788001981272,
1943 "promptEn": "basement in Shiganshina, the titan secret weighing down on you", 1961 "promptEn": "basement in Shiganshina, the titan secret weighing down on you",
1944 - "lastPlayedAt": 1788233150908 1962 + "lastPlayedAt": 1788239223427
1945 }, 1963 },
1946 { 1964 {
1947 "id": "7c2351a27a96", 1965 "id": "7c2351a27a96",
@@ -1971,7 +1989,7 @@ @@ -1971,7 +1989,7 @@
1971 "likes": 0, 1989 "likes": 0,
1972 "createdAt": 1788001428705, 1990 "createdAt": 1788001428705,
1973 "promptEn": "in the ruins of Konoha, exploiting a flaw to counter Pain", 1991 "promptEn": "in the ruins of Konoha, exploiting a flaw to counter Pain",
1974 - "lastPlayedAt": 1788229818554 1992 + "lastPlayedAt": 1788238687110
1975 }, 1993 },
1976 { 1994 {
1977 "id": "e486fab4c24d", 1995 "id": "e486fab4c24d",
@@ -1986,7 +2004,7 @@ @@ -1986,7 +2004,7 @@
1986 "likes": 0, 2004 "likes": 0,
1987 "createdAt": 1788001285796, 2005 "createdAt": 1788001285796,
1988 "promptEn": "in the ruins of Konoha, exploiting a flaw to counter Pain", 2006 "promptEn": "in the ruins of Konoha, exploiting a flaw to counter Pain",
1989 - "lastPlayedAt": 1788229776519 2007 + "lastPlayedAt": 1788236845633
1990 }, 2008 },
1991 { 2009 {
1992 "id": "808e89ca06a4", 2010 "id": "808e89ca06a4",
@@ -2001,7 +2019,7 @@ @@ -2001,7 +2019,7 @@
2001 "likes": 0, 2019 "likes": 0,
2002 "createdAt": 1788001049233, 2020 "createdAt": 1788001049233,
2003 "promptEn": "facing Pain, held the goal but paid a price", 2021 "promptEn": "facing Pain, held the goal but paid a price",
2004 - "lastPlayedAt": 1788229928658 2022 + "lastPlayedAt": 1788238809210
2005 }, 2023 },
2006 { 2024 {
2007 "id": "7ecd480a3c31", 2025 "id": "7ecd480a3c31",
@@ -2016,7 +2034,7 @@ @@ -2016,7 +2034,7 @@
2016 "likes": 0, 2034 "likes": 0,
2017 "createdAt": 1788000812932, 2035 "createdAt": 1788000812932,
2018 "promptEn": "facing Pain, went in head-on and got seen through", 2036 "promptEn": "facing Pain, went in head-on and got seen through",
2019 - "lastPlayedAt": 1788231423103 2037 + "lastPlayedAt": 1788237904442
2020 }, 2038 },
2021 { 2039 {
2022 "id": "f691ac021df0", 2040 "id": "f691ac021df0",
@@ -2031,7 +2049,7 @@ @@ -2031,7 +2049,7 @@
2031 "likes": 0, 2049 "likes": 0,
2032 "createdAt": 1788000577474, 2050 "createdAt": 1788000577474,
2033 "promptEn": "facing Pain, stayed passive and got cornered", 2051 "promptEn": "facing Pain, stayed passive and got cornered",
2034 - "lastPlayedAt": 1788230162864 2052 + "lastPlayedAt": 1788239588520
2035 }, 2053 },
2036 { 2054 {
2037 "id": "5ef9489bdc5b", 2055 "id": "5ef9489bdc5b",
@@ -2046,7 +2064,7 @@ @@ -2046,7 +2064,7 @@
2046 "likes": 0, 2064 "likes": 0,
2047 "createdAt": 1788000341927, 2065 "createdAt": 1788000341927,
2048 "promptEn": "Konoha destroyed by Shinra Tensei, Pain declaring order in the ruins", 2066 "promptEn": "Konoha destroyed by Shinra Tensei, Pain declaring order in the ruins",
2049 - "lastPlayedAt": 1788233757171 2067 + "lastPlayedAt": 1788237584239
2050 }, 2068 },
2051 { 2069 {
2052 "id": "e245dbcc6d36", 2070 "id": "e245dbcc6d36",
@@ -2061,7 +2079,7 @@ @@ -2061,7 +2079,7 @@
2061 "likes": 0, 2079 "likes": 0,
2062 "createdAt": 1788000105889, 2080 "createdAt": 1788000105889,
2063 "promptEn": "facing Sakazuki, exploiting a flaw to counter him", 2081 "promptEn": "facing Sakazuki, exploiting a flaw to counter him",
2064 - "lastPlayedAt": 1788232794712 2082 + "lastPlayedAt": 1788235920823
2065 }, 2083 },
2066 { 2084 {
2067 "id": "c9be7c07b793", 2085 "id": "c9be7c07b793",
@@ -2076,7 +2094,7 @@ @@ -2076,7 +2094,7 @@
2076 "likes": 0, 2094 "likes": 0,
2077 "createdAt": 1787999869694, 2095 "createdAt": 1787999869694,
2078 "promptEn": "facing Sakazuki, held the goal but paid a price", 2096 "promptEn": "facing Sakazuki, held the goal but paid a price",
2079 - "lastPlayedAt": 1788228957793 2097 + "lastPlayedAt": 1788238915275
2080 }, 2098 },
2081 { 2099 {
2082 "id": "677941e683c4", 2100 "id": "677941e683c4",
@@ -2091,7 +2109,7 @@ @@ -2091,7 +2109,7 @@
2091 "likes": 0, 2109 "likes": 0,
2092 "createdAt": 1787999633745, 2110 "createdAt": 1787999633745,
2093 "promptEn": "facing Sakazuki, went in head-on and got seen through", 2111 "promptEn": "facing Sakazuki, went in head-on and got seen through",
2094 - "lastPlayedAt": 1788232798715 2112 + "lastPlayedAt": 1788237468177
2095 }, 2113 },
2096 { 2114 {
2097 "id": "932d15f7f8bd", 2115 "id": "932d15f7f8bd",
@@ -2106,7 +2124,7 @@ @@ -2106,7 +2124,7 @@
2106 "likes": 0, 2124 "likes": 0,
2107 "createdAt": 1787999633670, 2125 "createdAt": 1787999633670,
2108 "promptEn": "facing Sakazuki, went in head-on and got seen through", 2126 "promptEn": "facing Sakazuki, went in head-on and got seen through",
2109 - "lastPlayedAt": 1788232700636 2127 + "lastPlayedAt": 1788238018530
2110 }, 2128 },
2111 { 2129 {
2112 "id": "a8b8b4430ee3", 2130 "id": "a8b8b4430ee3",
@@ -2121,7 +2139,7 @@ @@ -2121,7 +2139,7 @@
2121 "likes": 0, 2139 "likes": 0,
2122 "createdAt": 1787999397097, 2140 "createdAt": 1787999397097,
2123 "promptEn": "facing Sakazuki, stayed passive and got cornered", 2141 "promptEn": "facing Sakazuki, stayed passive and got cornered",
2124 - "lastPlayedAt": 1788229874611 2142 + "lastPlayedAt": 1788234753781
2125 }, 2143 },
2126 { 2144 {
2127 "id": "88923aaf57f3", 2145 "id": "88923aaf57f3",
@@ -2136,7 +2154,7 @@ @@ -2136,7 +2154,7 @@
2136 "likes": 0, 2154 "likes": 0,
2137 "createdAt": 1787999161013, 2155 "createdAt": 1787999161013,
2138 "promptEn": "execution platform at Marineford, Ace about to be executed, navy vs pirates", 2156 "promptEn": "execution platform at Marineford, Ace about to be executed, navy vs pirates",
2139 - "lastPlayedAt": 1788233208928 2157 + "lastPlayedAt": 1788237021780
2140 }, 2158 },
2141 { 2159 {
2142 "id": "00476b62bf75", 2160 "id": "00476b62bf75",
@@ -2151,7 +2169,7 @@ @@ -2151,7 +2169,7 @@
2151 "likes": 0, 2169 "likes": 0,
2152 "createdAt": 1787903583872, 2170 "createdAt": 1787903583872,
2153 "promptEn": "constable asks if she's always been alone here, she doesn't answer", 2171 "promptEn": "constable asks if she's always been alone here, she doesn't answer",
2154 - "lastPlayedAt": 1788232243824 2172 + "lastPlayedAt": 1788238082590
2155 }, 2173 },
2156 { 2174 {
2157 "id": "2ab2094e5679", 2175 "id": "2ab2094e5679",
@@ -2166,7 +2184,7 @@ @@ -2166,7 +2184,7 @@
2166 "likes": 0, 2184 "likes": 0,
2167 "createdAt": 1787900324972, 2185 "createdAt": 1787900324972,
2168 "promptEn": "constable declines a drink, asks what this place is, she watches him through the screen", 2186 "promptEn": "constable declines a drink, asks what this place is, she watches him through the screen",
2169 - "lastPlayedAt": 1788229530286 2187 + "lastPlayedAt": 1788234247395
2170 }, 2188 },
2171 { 2189 {
2172 "id": "43ee5c728f1b", 2190 "id": "43ee5c728f1b",
@@ -2181,7 +2199,7 @@ @@ -2181,7 +2199,7 @@
2181 "likes": 0, 2199 "likes": 0,
2182 "createdAt": 1787850191899, 2200 "createdAt": 1787850191899,
2183 "promptEn": "constable reaches out to touch her hand through the screen gap, no response", 2201 "promptEn": "constable reaches out to touch her hand through the screen gap, no response",
2184 - "lastPlayedAt": 1788229017844 2202 + "lastPlayedAt": 1788236525342
2185 }, 2203 },
2186 { 2204 {
2187 "id": "9bd8c9fbf1f6", 2205 "id": "9bd8c9fbf1f6",
@@ -2196,7 +2214,7 @@ @@ -2196,7 +2214,7 @@
2196 "likes": 0, 2214 "likes": 0,
2197 "createdAt": 1787847447661, 2215 "createdAt": 1787847447661,
2198 "promptEn": "constable tells the woman behind the screen he'll trust her tonight", 2216 "promptEn": "constable tells the woman behind the screen he'll trust her tonight",
2199 - "lastPlayedAt": 1788232414433 2217 + "lastPlayedAt": 1788239648569
2200 }, 2218 },
2201 { 2219 {
2202 "id": "8cb493338cb3", 2220 "id": "8cb493338cb3",
@@ -2211,7 +2229,7 @@ @@ -2211,7 +2229,7 @@
2211 "likes": 0, 2229 "likes": 0,
2212 "createdAt": 1787831362083, 2230 "createdAt": 1787831362083,
2213 "promptEn": "constable promises to get her out safely if she trusts him", 2231 "promptEn": "constable promises to get her out safely if she trusts him",
2214 - "lastPlayedAt": 1788233483044 2232 + "lastPlayedAt": 1788237063824
2215 }, 2233 },
2216 { 2234 {
2217 "id": "5ede61c01b22", 2235 "id": "5ede61c01b22",
@@ -2226,7 +2244,7 @@ @@ -2226,7 +2244,7 @@
2226 "likes": 0, 2244 "likes": 0,
2227 "createdAt": 1787762342686, 2245 "createdAt": 1787762342686,
2228 "promptEn": "woman tells him from behind the screen to go check outside, he steps out warily", 2246 "promptEn": "woman tells him from behind the screen to go check outside, he steps out warily",
2229 - "lastPlayedAt": 1788233421018 2247 + "lastPlayedAt": 1788239339500
2230 }, 2248 },
2231 { 2249 {
2232 "id": "47a6de809c61", 2250 "id": "47a6de809c61",
@@ -2241,7 +2259,7 @@ @@ -2241,7 +2259,7 @@
2241 "likes": 0, 2259 "likes": 0,
2242 "createdAt": 1787762332289, 2260 "createdAt": 1787762332289,
2243 "promptEn": "woman behind the screen stirs and softly reassures him", 2261 "promptEn": "woman behind the screen stirs and softly reassures him",
2244 - "lastPlayedAt": 1788231953653 2262 + "lastPlayedAt": 1788237304019
2245 }, 2263 },
2246 { 2264 {
2247 "id": "0abf6e529c92", 2265 "id": "0abf6e529c92",
@@ -2256,7 +2274,7 @@ @@ -2256,7 +2274,7 @@
2256 "likes": 0, 2274 "likes": 0,
2257 "createdAt": 1787762311804, 2275 "createdAt": 1787762311804,
2258 "promptEn": "constable sits with a cup of water, slowly looking up at the woman behind the screen", 2276 "promptEn": "constable sits with a cup of water, slowly looking up at the woman behind the screen",
2259 - "lastPlayedAt": 1788232819824 2277 + "lastPlayedAt": 1788237147890
2260 }, 2278 },
2261 { 2279 {
2262 "id": "850f13881576", 2280 "id": "850f13881576",
@@ -2271,7 +2289,7 @@ @@ -2271,7 +2289,7 @@
2271 "likes": 0, 2289 "likes": 0,
2272 "createdAt": 1787762286261, 2290 "createdAt": 1787762286261,
2273 "promptEn": "constable stares at her through the screen, silent, saying nothing", 2291 "promptEn": "constable stares at her through the screen, silent, saying nothing",
2274 - "lastPlayedAt": 1788230968864 2292 + "lastPlayedAt": 1788237231957
2275 }, 2293 },
2276 { 2294 {
2277 "id": "59711a102e84", 2295 "id": "59711a102e84",
@@ -2286,7 +2304,7 @@ @@ -2286,7 +2304,7 @@
2286 "likes": 0, 2304 "likes": 0,
2287 "createdAt": 1787762224218, 2305 "createdAt": 1787762224218,
2288 "promptEn": "constable stands under the lamp, holding her gaze through the screen, not backing away", 2306 "promptEn": "constable stands under the lamp, holding her gaze through the screen, not backing away",
2289 - "lastPlayedAt": 1788232250345 2307 + "lastPlayedAt": 1788239794026
2290 }, 2308 },
2291 { 2309 {
2292 "id": "f2e70205766b", 2310 "id": "f2e70205766b",
@@ -2301,7 +2319,7 @@ @@ -2301,7 +2319,7 @@
2301 "likes": 0, 2319 "likes": 0,
2302 "createdAt": 1787738044140, 2320 "createdAt": 1787738044140,
2303 "promptEn": "constable carefully checks the door and window latches, she watches from behind the screen", 2321 "promptEn": "constable carefully checks the door and window latches, she watches from behind the screen",
2304 - "lastPlayedAt": 1788233721152 2322 + "lastPlayedAt": 1788239592524
2305 }, 2323 },
2306 { 2324 {
2307 "id": "cfbef6e37ae4", 2325 "id": "cfbef6e37ae4",
@@ -2316,7 +2334,7 @@ @@ -2316,7 +2334,7 @@
2316 "likes": 0, 2334 "likes": 0,
2317 "createdAt": 1787730413689, 2335 "createdAt": 1787730413689,
2318 "promptEn": "constable grabs the oil lamp and hurls it at the portrait, the scroll catches fire", 2336 "promptEn": "constable grabs the oil lamp and hurls it at the portrait, the scroll catches fire",
2319 - "lastPlayedAt": 1788233781184 2337 + "lastPlayedAt": 1788236819612
2320 }, 2338 },
2321 { 2339 {
2322 "id": "e6c23c6adc42", 2340 "id": "e6c23c6adc42",
@@ -2331,7 +2349,7 @@ @@ -2331,7 +2349,7 @@
2331 "likes": 0, 2349 "likes": 0,
2332 "createdAt": 1787730331754, 2350 "createdAt": 1787730331754,
2333 "promptEn": "constable steps in from the rainy night, spots her, introduces himself with a cover story", 2351 "promptEn": "constable steps in from the rainy night, spots her, introduces himself with a cover story",
2334 - "lastPlayedAt": 1788232642592 2352 + "lastPlayedAt": 1788239748660
2335 }, 2353 },
2336 { 2354 {
2337 "id": "d720d52c783f", 2355 "id": "d720d52c783f",
@@ -2346,7 +2364,7 @@ @@ -2346,7 +2364,7 @@
2346 "likes": 0, 2364 "likes": 0,
2347 "createdAt": 1787730331663, 2365 "createdAt": 1787730331663,
2348 "promptEn": "constable steps in from the rainy night, spots her, introduces himself with a cover story", 2366 "promptEn": "constable steps in from the rainy night, spots her, introduces himself with a cover story",
2349 - "lastPlayedAt": 1788232982830 2367 + "lastPlayedAt": 1788237267984
2350 }, 2368 },
2351 { 2369 {
2352 "id": "84e8337de242", 2370 "id": "84e8337de242",
@@ -2361,7 +2379,7 @@ @@ -2361,7 +2379,7 @@
2361 "likes": 0, 2379 "likes": 0,
2362 "createdAt": 1787724313620, 2380 "createdAt": 1787724313620,
2363 "promptEn": "first-person view bursting into the room, glancing back in fear, the woman startled", 2381 "promptEn": "first-person view bursting into the room, glancing back in fear, the woman startled",
2364 - "lastPlayedAt": 1788232846754 2382 + "lastPlayedAt": 1788239682009
2365 }, 2383 },
2366 { 2384 {
2367 "id": "81b6c3789111", 2385 "id": "81b6c3789111",
@@ -2391,7 +2409,7 @@ @@ -2391,7 +2409,7 @@
2391 "likes": 0, 2409 "likes": 0,
2392 "createdAt": 1787708701547, 2410 "createdAt": 1787708701547,
2393 "promptEn": "constable gently tells her he'll stay and keep her company", 2411 "promptEn": "constable gently tells her he'll stay and keep her company",
2394 - "lastPlayedAt": 1788232778701 2412 + "lastPlayedAt": 1788237740325
2395 }, 2413 },
2396 { 2414 {
2397 "id": "8d9c1dad041f", 2415 "id": "8d9c1dad041f",
@@ -2406,7 +2424,7 @@ @@ -2406,7 +2424,7 @@
2406 "likes": 0, 2424 "likes": 0,
2407 "createdAt": 1787708686397, 2425 "createdAt": 1787708686397,
2408 "promptEn": "constable accepts the invitation, moves to sit on a stool closer to the screen", 2426 "promptEn": "constable accepts the invitation, moves to sit on a stool closer to the screen",
2409 - "lastPlayedAt": 1788231192963 2427 + "lastPlayedAt": 1788236026908
2410 }, 2428 },
2411 { 2429 {
2412 "id": "ad611bcbd06b", 2430 "id": "ad611bcbd06b",
@@ -2436,7 +2454,7 @@ @@ -2436,7 +2454,7 @@
2436 "likes": 0, 2454 "likes": 0,
2437 "createdAt": 1787708674508, 2455 "createdAt": 1787708674508,
2438 "promptEn": "constable sees through her painted disguise and rips her face off, revealing a demonic visage underneath", 2456 "promptEn": "constable sees through her painted disguise and rips her face off, revealing a demonic visage underneath",
2439 - "lastPlayedAt": 1788231877578 2457 + "lastPlayedAt": 1788234739771
2440 }, 2458 },
2441 { 2459 {
2442 "id": "6d433dcc104c", 2460 "id": "6d433dcc104c",
@@ -2451,7 +2469,7 @@ @@ -2451,7 +2469,7 @@
2451 "likes": 0, 2469 "likes": 0,
2452 "createdAt": 1787708663418, 2470 "createdAt": 1787708663418,
2453 "promptEn": "constable catches his breath, thanks her, and rests on the couch while she watches", 2471 "promptEn": "constable catches his breath, thanks her, and rests on the couch while she watches",
2454 - "lastPlayedAt": 1788229822555 2472 + "lastPlayedAt": 1788239175402
2455 }, 2473 },
2456 { 2474 {
2457 "id": "cd22c2b491fc", 2475 "id": "cd22c2b491fc",
@@ -2466,7 +2484,7 @@ @@ -2466,7 +2484,7 @@
2466 "likes": 0, 2484 "likes": 0,
2467 "createdAt": 1787708653181, 2485 "createdAt": 1787708653181,
2468 "promptEn": "constable holds her gaze, quietly says \"go on\", then waits in silence", 2486 "promptEn": "constable holds her gaze, quietly says \"go on\", then waits in silence",
2469 - "lastPlayedAt": 1788231861563 2487 + "lastPlayedAt": 1788235216202
2470 }, 2488 },
2471 { 2489 {
2472 "id": "55f98ceec9e4", 2490 "id": "55f98ceec9e4",
@@ -2481,7 +2499,7 @@ @@ -2481,7 +2499,7 @@
2481 "likes": 0, 2499 "likes": 0,
2482 "createdAt": 1787708648409, 2500 "createdAt": 1787708648409,
2483 "promptEn": "she reaches a hand through the screen gap beckoning him, he steps back and firmly refuses", 2501 "promptEn": "she reaches a hand through the screen gap beckoning him, he steps back and firmly refuses",
2484 - "lastPlayedAt": 1788232290370 2502 + "lastPlayedAt": 1788239079356
2485 }, 2503 },
2486 { 2504 {
2487 "id": "3f0c28f5373a", 2505 "id": "3f0c28f5373a",
@@ -2496,7 +2514,7 @@ @@ -2496,7 +2514,7 @@
2496 "likes": 0, 2514 "likes": 0,
2497 "createdAt": 1787708646888, 2515 "createdAt": 1787708646888,
2498 "promptEn": "constable studies her through the screen growing suspicious, hand drifting to his sword", 2516 "promptEn": "constable studies her through the screen growing suspicious, hand drifting to his sword",
2499 - "lastPlayedAt": 1788231483166 2517 + "lastPlayedAt": 1788239409555
2500 }, 2518 },
2501 { 2519 {
2502 "id": "1c0c203cfb3b", 2520 "id": "1c0c203cfb3b",
@@ -2511,7 +2529,7 @@ @@ -2511,7 +2529,7 @@
2511 "likes": 0, 2529 "likes": 0,
2512 "createdAt": 1787708641782, 2530 "createdAt": 1787708641782,
2513 "promptEn": "constable sits down at the table, studying her expression through the screen", 2531 "promptEn": "constable sits down at the table, studying her expression through the screen",
2514 - "lastPlayedAt": 1788229894631 2532 + "lastPlayedAt": 1788238627059
2515 }, 2533 },
2516 { 2534 {
2517 "id": "89975be9def4", 2535 "id": "89975be9def4",
@@ -2526,7 +2544,7 @@ @@ -2526,7 +2544,7 @@
2526 "likes": 0, 2544 "likes": 0,
2527 "createdAt": 1787708626228, 2545 "createdAt": 1787708626228,
2528 "promptEn": "constable drinks some water first, thanks her, watching the screen warily as he drinks", 2546 "promptEn": "constable drinks some water first, thanks her, watching the screen warily as he drinks",
2529 - "lastPlayedAt": 1788232223822 2547 + "lastPlayedAt": 1788237762334
2530 }, 2548 },
2531 { 2549 {
2532 "id": "e08f7f7e54dd", 2550 "id": "e08f7f7e54dd",
@@ -2541,7 +2559,7 @@ @@ -2541,7 +2559,7 @@
2541 "likes": 0, 2559 "likes": 0,
2542 "createdAt": 1787708622306, 2560 "createdAt": 1787708622306,
2543 "promptEn": "constable gives a clasped-hand salute and thanks her, she gives a subtle nod", 2561 "promptEn": "constable gives a clasped-hand salute and thanks her, she gives a subtle nod",
2544 - "lastPlayedAt": 1788229043869 2562 + "lastPlayedAt": 1788236275114
2545 }, 2563 },
2546 { 2564 {
2547 "id": "538dc7e5746f", 2565 "id": "538dc7e5746f",
@@ -2556,7 +2574,7 @@ @@ -2556,7 +2574,7 @@
2556 "likes": 0, 2574 "likes": 0,
2557 "createdAt": 1787708622178, 2575 "createdAt": 1787708622178,
2558 "promptEn": "constable gives a clasped-hand salute and thanks her, she gives a subtle nod", 2576 "promptEn": "constable gives a clasped-hand salute and thanks her, she gives a subtle nod",
2559 - "lastPlayedAt": 1788231903603 2577 + "lastPlayedAt": 1788236299134
2560 }, 2578 },
2561 { 2579 {
2562 "id": "1721c75dbc3b", 2580 "id": "1721c75dbc3b",
@@ -2571,7 +2589,7 @@ @@ -2571,7 +2589,7 @@
2571 "likes": 0, 2589 "likes": 0,
2572 "createdAt": 1787708615415, 2590 "createdAt": 1787708615415,
2573 "promptEn": "she slides her hand through the screen gap asking him to warm it, he hesitates without touching", 2591 "promptEn": "she slides her hand through the screen gap asking him to warm it, he hesitates without touching",
2574 - "lastPlayedAt": 1788230768774 2592 + "lastPlayedAt": 1788238224719
2575 }, 2593 },
2576 { 2594 {
2577 "id": "c2b145b111e2", 2595 "id": "c2b145b111e2",
@@ -2586,7 +2604,7 @@ @@ -2586,7 +2604,7 @@
2586 "likes": 0, 2604 "likes": 0,
2587 "createdAt": 1787708613364, 2605 "createdAt": 1787708613364,
2588 "promptEn": "constable steps in, notices her behind the screen, greets her, she just watches silently", 2606 "promptEn": "constable steps in, notices her behind the screen, greets her, she just watches silently",
2589 - "lastPlayedAt": 1788232155826 2607 + "lastPlayedAt": 1788238833238
2590 }, 2608 },
2591 { 2609 {
2592 "id": "a5d5d353bbf8", 2610 "id": "a5d5d353bbf8",
@@ -2601,7 +2619,7 @@ @@ -2601,7 +2619,7 @@
2601 "likes": 0, 2619 "likes": 0,
2602 "createdAt": 1787674768144, 2620 "createdAt": 1787674768144,
2603 "promptEn": "constable lifts a bronze mirror, reflecting candlelight onto the portrait on the wall", 2621 "promptEn": "constable lifts a bronze mirror, reflecting candlelight onto the portrait on the wall",
2604 - "lastPlayedAt": 1788231997701 2622 + "lastPlayedAt": 1788237542221
2605 }, 2623 },
2606 { 2624 {
2607 "id": "3be1f275fde2", 2625 "id": "3be1f275fde2",
@@ -2616,7 +2634,7 @@ @@ -2616,7 +2634,7 @@
2616 "likes": 0, 2634 "likes": 0,
2617 "createdAt": 1787674743188, 2635 "createdAt": 1787674743188,
2618 "promptEn": "constable draws his sword and breaks the sealed talisman on the screen, revealing her behind it", 2636 "promptEn": "constable draws his sword and breaks the sealed talisman on the screen, revealing her behind it",
2619 - "lastPlayedAt": 1788233395001 2637 + "lastPlayedAt": 1788238747163
2620 }, 2638 },
2621 { 2639 {
2622 "id": "693e0b9ca66a", 2640 "id": "693e0b9ca66a",
@@ -2631,7 +2649,7 @@ @@ -2631,7 +2649,7 @@
2631 "likes": 0, 2649 "likes": 0,
2632 "createdAt": 1787674737743, 2650 "createdAt": 1787674737743,
2633 "promptEn": "constable slowly looks around the room, discovers her watching him motionlessly through the screen", 2651 "promptEn": "constable slowly looks around the room, discovers her watching him motionlessly through the screen",
2634 - "lastPlayedAt": 1788230912836 2652 + "lastPlayedAt": 1788238877259
2635 }, 2653 },
2636 { 2654 {
2637 "id": "c74118d0413f", 2655 "id": "c74118d0413f",
@@ -2646,7 +2664,7 @@ @@ -2646,7 +2664,7 @@
2646 "likes": 0, 2664 "likes": 0,
2647 "createdAt": 1787674716075, 2665 "createdAt": 1787674716075,
2648 "promptEn": "constable watches the oil lamp flame flicker, her silhouette motionless behind the screen", 2666 "promptEn": "constable watches the oil lamp flame flicker, her silhouette motionless behind the screen",
2649 - "lastPlayedAt": 1788232920805 2667 + "lastPlayedAt": 1788239041338
2650 }, 2668 },
2651 { 2669 {
2652 "id": "23d1ae1982e6", 2670 "id": "23d1ae1982e6",
@@ -2661,7 +2679,7 @@ @@ -2661,7 +2679,7 @@
2661 "likes": 0, 2679 "likes": 0,
2662 "createdAt": 1787674713724, 2680 "createdAt": 1787674713724,
2663 "promptEn": "constable enters and cautiously looks around, his gaze settling on her silhouette behind the screen", 2681 "promptEn": "constable enters and cautiously looks around, his gaze settling on her silhouette behind the screen",
2664 - "lastPlayedAt": 1788233370997 2682 + "lastPlayedAt": 1788239716626
2665 }, 2683 },
2666 { 2684 {
2667 "id": "b69389094416", 2685 "id": "b69389094416",
@@ -2676,7 +2694,7 @@ @@ -2676,7 +2694,7 @@
2676 "likes": 0, 2694 "likes": 0,
2677 "createdAt": 1787674695703, 2695 "createdAt": 1787674695703,
2678 "promptEn": "constable stops, faces the screen, quietly promises to keep his voice down", 2696 "promptEn": "constable stops, faces the screen, quietly promises to keep his voice down",
2679 - "lastPlayedAt": 1788231753466 2697 + "lastPlayedAt": 1788238863250
2680 }, 2698 },
2681 { 2699 {
2682 "id": "94dc455e26bf", 2700 "id": "94dc455e26bf",
@@ -2691,7 +2709,7 @@ @@ -2691,7 +2709,7 @@
2691 "likes": 0, 2709 "likes": 0,
2692 "createdAt": 1787674688717, 2710 "createdAt": 1787674688717,
2693 "promptEn": "constable agrees to stay, lets his guard down, she gives a barely-there nod", 2711 "promptEn": "constable agrees to stay, lets his guard down, she gives a barely-there nod",
2694 - "lastPlayedAt": 1788233705137 2712 + "lastPlayedAt": 1788238939281
2695 }, 2713 },
2696 { 2714 {
2697 "id": "4874fe3fdbc9", 2715 "id": "4874fe3fdbc9",
@@ -2706,7 +2724,7 @@ @@ -2706,7 +2724,7 @@
2706 "likes": 0, 2724 "likes": 0,
2707 "createdAt": 1787674660698, 2725 "createdAt": 1787674660698,
2708 "promptEn": "constable smashes the bronze mirror on the table, it shatters, she watches from behind the screen", 2726 "promptEn": "constable smashes the bronze mirror on the table, it shatters, she watches from behind the screen",
2709 - "lastPlayedAt": 1788228043003 2727 + "lastPlayedAt": 1788238705131
2710 }, 2728 },
2711 { 2729 {
2712 "id": "ed77b83851a2", 2730 "id": "ed77b83851a2",
@@ -2721,7 +2739,7 @@ @@ -2721,7 +2739,7 @@
2721 "likes": 0, 2739 "likes": 0,
2722 "createdAt": 1787674642139, 2740 "createdAt": 1787674642139,
2723 "promptEn": "constable forms hand seals and summons a giant toad out of smoke", 2741 "promptEn": "constable forms hand seals and summons a giant toad out of smoke",
2724 - "lastPlayedAt": 1788228853703 2742 + "lastPlayedAt": 1788237684292
2725 }, 2743 },
2726 { 2744 {
2727 "id": "e9a6320e18a9", 2745 "id": "e9a6320e18a9",
@@ -2736,7 +2754,7 @@ @@ -2736,7 +2754,7 @@
2736 "likes": 0, 2754 "likes": 0,
2737 "createdAt": 1787674642026, 2755 "createdAt": 1787674642026,
2738 "promptEn": "constable enters from the side and sits down on a stool by the lamp to rest", 2756 "promptEn": "constable enters from the side and sits down on a stool by the lamp to rest",
2739 - "lastPlayedAt": 1788233801194 2757 + "lastPlayedAt": 1788236343178
2740 }, 2758 },
2741 { 2759 {
2742 "id": "fe7ffbe65653", 2760 "id": "fe7ffbe65653",
@@ -2766,7 +2784,7 @@ @@ -2766,7 +2784,7 @@
2766 "likes": 0, 2784 "likes": 0,
2767 "createdAt": 1787674623678, 2785 "createdAt": 1787674623678,
2768 "promptEn": "constable bows to the screen, thanking her for sheltering him", 2786 "promptEn": "constable bows to the screen, thanking her for sheltering him",
2769 - "lastPlayedAt": 1788230802785 2787 + "lastPlayedAt": 1788239287469
2770 }, 2788 },
2771 { 2789 {
2772 "id": "d94991a1e21b", 2790 "id": "d94991a1e21b",
@@ -2781,7 +2799,7 @@ @@ -2781,7 +2799,7 @@
2781 "likes": 0, 2799 "likes": 0,
2782 "createdAt": 1787674609636, 2800 "createdAt": 1787674609636,
2783 "promptEn": "constable silently watches her through the screen gaps, camera slowly zooming in on her face", 2801 "promptEn": "constable silently watches her through the screen gaps, camera slowly zooming in on her face",
2784 - "lastPlayedAt": 1788233276957 2802 + "lastPlayedAt": 1788239155387
2785 }, 2803 },
2786 { 2804 {
2787 "id": "5e53f7f5fe9b", 2805 "id": "5e53f7f5fe9b",
@@ -2796,7 +2814,7 @@ @@ -2796,7 +2814,7 @@
2796 "likes": 0, 2814 "likes": 0,
2797 "createdAt": 1787674446841, 2815 "createdAt": 1787674446841,
2798 "promptEn": "player panics into the room, glances back, out of breath, says something's chasing him outside", 2816 "promptEn": "player panics into the room, glances back, out of breath, says something's chasing him outside",
2799 - "lastPlayedAt": 1788232872779 2817 + "lastPlayedAt": 1788238603043
2800 }, 2818 },
2801 { 2819 {
2802 "id": "c25d53a5e086", 2820 "id": "c25d53a5e086",
@@ -2811,7 +2829,7 @@ @@ -2811,7 +2829,7 @@
2811 "likes": 0, 2829 "likes": 0,
2812 "createdAt": 1787674395006, 2830 "createdAt": 1787674395006,
2813 "promptEn": "constable and the woman hold a tense silent stare through the screen", 2831 "promptEn": "constable and the woman hold a tense silent stare through the screen",
2814 - "lastPlayedAt": 1788229934661 2832 + "lastPlayedAt": 1788238004523
2815 }, 2833 },
2816 { 2834 {
2817 "id": "f264bce22903", 2835 "id": "f264bce22903",
@@ -2826,7 +2844,7 @@ @@ -2826,7 +2844,7 @@
2826 "likes": 0, 2844 "likes": 0,
2827 "createdAt": 1787674361187, 2845 "createdAt": 1787674361187,
2828 "promptEn": "constable presses his hand to his chest, vowing his heart to her, she gives a knowing smile", 2846 "promptEn": "constable presses his hand to his chest, vowing his heart to her, she gives a knowing smile",
2829 - "lastPlayedAt": 1788233038859 2847 + "lastPlayedAt": 1788238671098
2830 }, 2848 },
2831 { 2849 {
2832 "id": "8aba0a6bf05b", 2850 "id": "8aba0a6bf05b",
@@ -2841,7 +2859,7 @@ @@ -2841,7 +2859,7 @@
2841 "likes": 0, 2859 "likes": 0,
2842 "createdAt": 1787674351536, 2860 "createdAt": 1787674351536,
2843 "promptEn": "constable searches an old house at night, candle flickering, woman hiding behind the screen", 2861 "promptEn": "constable searches an old house at night, candle flickering, woman hiding behind the screen",
2844 - "lastPlayedAt": 1788232892795 2862 + "lastPlayedAt": 1788237512206
2845 }, 2863 },
2846 { 2864 {
2847 "id": "dc13e4f165dc", 2865 "id": "dc13e4f165dc",
@@ -2856,7 +2874,7 @@ @@ -2856,7 +2874,7 @@
2856 "likes": 0, 2874 "likes": 0,
2857 "createdAt": 1787672849330, 2875 "createdAt": 1787672849330,
2858 "promptEn": "constable cautiously follows her silent gesture, hand near his sword, tracking her movement", 2876 "promptEn": "constable cautiously follows her silent gesture, hand near his sword, tracking her movement",
2859 - "lastPlayedAt": 1788232675826 2877 + "lastPlayedAt": 1788236149011
2860 }, 2878 },
2861 { 2879 {
2862 "id": "feb4df69e1a2", 2880 "id": "feb4df69e1a2",
@@ -2870,7 +2888,8 @@ @@ -2870,7 +2888,8 @@
2870 "partition": "ref2va", 2888 "partition": "ref2va",
2871 "likes": 0, 2889 "likes": 0,
2872 "createdAt": 1787665100787, 2890 "createdAt": 1787665100787,
2873 - "promptEn": "constable politely asks the woman behind the screen for tea" 2891 + "promptEn": "constable politely asks the woman behind the screen for tea",
  2892 + "lastPlayedAt": 1788237217948
2874 }, 2893 },
2875 { 2894 {
2876 "id": "d2c77b87b84b", 2895 "id": "d2c77b87b84b",
@@ -2884,7 +2903,7 @@ @@ -2884,7 +2903,7 @@
2884 "partition": "ref2va", 2903 "partition": "ref2va",
2885 "likes": 0, 2904 "likes": 0,
2886 "createdAt": 1787652321725, 2905 "createdAt": 1787652321725,
2887 - "lastPlayedAt": 1788232862770, 2906 + "lastPlayedAt": 1788239762009,
2888 "promptEn": "constable talking to the woman through the screen" 2907 "promptEn": "constable talking to the woman through the screen"
2889 }, 2908 },
2890 { 2909 {
@@ -2900,7 +2919,7 @@ @@ -2900,7 +2919,7 @@
2900 "likes": 0, 2919 "likes": 0,
2901 "createdAt": 1787642579766, 2920 "createdAt": 1787642579766,
2902 "promptEn": "a summoner chants and calls a giant toad, which swallows the white-robed ghost whole", 2921 "promptEn": "a summoner chants and calls a giant toad, which swallows the white-robed ghost whole",
2903 - "lastPlayedAt": 1788231783491 2922 + "lastPlayedAt": 1788238761173
2904 }, 2923 },
2905 { 2924 {
2906 "id": "22a666455198", 2925 "id": "22a666455198",
@@ -2914,7 +2933,8 @@ @@ -2914,7 +2933,8 @@
2914 "partition": "ref2va", 2933 "partition": "ref2va",
2915 "likes": 0, 2934 "likes": 0,
2916 "createdAt": 1787640567001, 2935 "createdAt": 1787640567001,
2917 - "promptEn": "summoned toad Wentai, beat the painted-skin female ghost" 2936 + "promptEn": "summoned toad Wentai, beat the painted-skin female ghost",
  2937 + "lastPlayedAt": 1788238370847
2918 }, 2938 },
2919 { 2939 {
2920 "id": "1cc42a8669a1", 2940 "id": "1cc42a8669a1",
@@ -2928,7 +2948,7 @@ @@ -2928,7 +2948,7 @@
2928 "partition": "ref2va", 2948 "partition": "ref2va",
2929 "likes": 0, 2949 "likes": 0,
2930 "createdAt": 1787639957338, 2950 "createdAt": 1787639957338,
2931 - "lastPlayedAt": 1788232935826, 2951 + "lastPlayedAt": 1788239301475,
2932 "promptEn": "constable talking to the woman through the screen" 2952 "promptEn": "constable talking to the woman through the screen"
2933 }, 2953 },
2934 { 2954 {
@@ -2944,7 +2964,7 @@ @@ -2944,7 +2964,7 @@
2944 "likes": 0, 2964 "likes": 0,
2945 "createdAt": 1787637292741, 2965 "createdAt": 1787637292741,
2946 "promptEn": "constable bursts in panicked, spots the woman behind the screen", 2966 "promptEn": "constable bursts in panicked, spots the woman behind the screen",
2947 - "lastPlayedAt": 1788233080873 2967 + "lastPlayedAt": 1788236779575
2948 }, 2968 },
2949 { 2969 {
2950 "id": "93c2b84a2a10", 2970 "id": "93c2b84a2a10",
@@ -2958,7 +2978,8 @@ @@ -2958,7 +2978,8 @@
2958 "partition": "ref2va", 2978 "partition": "ref2va",
2959 "likes": 0, 2979 "likes": 0,
2960 "createdAt": 1787635935502, 2980 "createdAt": 1787635935502,
2961 - "promptEn": "constable bursts in panicked, spots the woman behind the screen" 2981 + "promptEn": "constable bursts in panicked, spots the woman behind the screen",
  2982 + "lastPlayedAt": 1788239035337
2962 }, 2983 },
2963 { 2984 {
2964 "id": "b32769bf80db", 2985 "id": "b32769bf80db",
@@ -2973,7 +2994,7 @@ @@ -2973,7 +2994,7 @@
2973 "likes": 0, 2994 "likes": 0,
2974 "createdAt": 1787632587351, 2995 "createdAt": 1787632587351,
2975 "promptEn": "constable bursts in panicked, spots the woman behind the screen", 2996 "promptEn": "constable bursts in panicked, spots the woman behind the screen",
2976 - "lastPlayedAt": 1788232814731 2997 + "lastPlayedAt": 1788236799594
2977 }, 2998 },
2978 { 2999 {
2979 "id": "282281777d1b", 3000 "id": "282281777d1b",
@@ -2987,7 +3008,8 @@ @@ -2987,7 +3008,8 @@
2987 "partition": "text", 3008 "partition": "text",
2988 "likes": 0, 3009 "likes": 0,
2989 "createdAt": 1787615243753, 3010 "createdAt": 1787615243753,
2990 - "promptEn": "robot arm picks up a red block and puts it down" 3011 + "promptEn": "robot arm picks up a red block and puts it down",
  3012 + "lastPlayedAt": 1788238791204
2991 }, 3013 },
2992 { 3014 {
2993 "id": "d040eec16a88", 3015 "id": "d040eec16a88",
@@ -3002,7 +3024,7 @@ @@ -3002,7 +3024,7 @@
3002 "likes": 0, 3024 "likes": 0,
3003 "createdAt": 1787615242602, 3025 "createdAt": 1787615242602,
3004 "promptEn": "otome game male lead showcase PV", 3026 "promptEn": "otome game male lead showcase PV",
3005 - "lastPlayedAt": 1788233659118 3027 + "lastPlayedAt": 1788239449554
3006 }, 3028 },
3007 { 3029 {
3008 "id": "423a20afba5a", 3030 "id": "423a20afba5a",
@@ -3016,7 +3038,8 @@ @@ -3016,7 +3038,8 @@
3016 "partition": "ref2va", 3038 "partition": "ref2va",
3017 "likes": 0, 3039 "likes": 0,
3018 "createdAt": 1787615241814, 3040 "createdAt": 1787615241814,
3019 - "promptEn": "xianxia character turning, only back and side profile shown" 3041 + "promptEn": "xianxia character turning, only back and side profile shown",
  3042 + "lastPlayedAt": 1788239247441
3020 }, 3043 },
3021 { 3044 {
3022 "id": "1a398591663e", 3045 "id": "1a398591663e",
@@ -3031,7 +3054,7 @@ @@ -3031,7 +3054,7 @@
3031 "likes": 0, 3054 "likes": 0,
3032 "createdAt": 1787615240988, 3055 "createdAt": 1787615240988,
3033 "promptEn": "clay fox sprints off a cliff, slow-mo leap over the canyon", 3056 "promptEn": "clay fox sprints off a cliff, slow-mo leap over the canyon",
3034 - "lastPlayedAt": 1788232611825 3057 + "lastPlayedAt": 1788239093359
3035 }, 3058 },
3036 { 3059 {
3037 "id": "db125dc6a831", 3060 "id": "db125dc6a831",
@@ -3045,7 +3068,7 @@ @@ -3045,7 +3068,7 @@
3045 "partition": "text", 3068 "partition": "text",
3046 "likes": 0, 3069 "likes": 0,
3047 "createdAt": 1787615240199, 3070 "createdAt": 1787615240199,
3048 - "lastPlayedAt": 1788230818793, 3071 + "lastPlayedAt": 1788239558507,
3049 "promptEn": "website UI title slides in, car light fades to red" 3072 "promptEn": "website UI title slides in, car light fades to red"
3050 }, 3073 },
3051 { 3074 {
@@ -3061,7 +3084,7 @@ @@ -3061,7 +3084,7 @@
3061 "likes": 0, 3084 "likes": 0,
3062 "createdAt": 1787588547172, 3085 "createdAt": 1787588547172,
3063 "promptEn": "product website scrolling down with flashy effects", 3086 "promptEn": "product website scrolling down with flashy effects",
3064 - "lastPlayedAt": 1788232013692 3087 + "lastPlayedAt": 1788236609419
3065 }, 3088 },
3066 { 3089 {
3067 "id": "2e18bf09b46a", 3090 "id": "2e18bf09b46a",
@@ -3075,7 +3098,8 @@ @@ -3075,7 +3098,8 @@
3075 "partition": "ref2va", 3098 "partition": "ref2va",
3076 "likes": 0, 3099 "likes": 0,
3077 "createdAt": 1787588545801, 3100 "createdAt": 1787588545801,
3078 - "promptEn": "game menu UI, character swaps out a mechanical arm" 3101 + "promptEn": "game menu UI, character swaps out a mechanical arm",
  3102 + "lastPlayedAt": 1788236467285
3079 }, 3103 },
3080 { 3104 {
3081 "id": "fe749aa533a0", 3105 "id": "fe749aa533a0",
@@ -3090,7 +3114,7 @@ @@ -3090,7 +3114,7 @@
3090 "likes": 0, 3114 "likes": 0,
3091 "createdAt": 1787588545243, 3115 "createdAt": 1787588545243,
3092 "promptEn": "ergonomic office chair 360 showcase ad", 3116 "promptEn": "ergonomic office chair 360 showcase ad",
3093 - "lastPlayedAt": 1788231939637 3117 + "lastPlayedAt": 1788236183042
3094 }, 3118 },
3095 { 3119 {
3096 "id": "b0d258352c3c", 3120 "id": "b0d258352c3c",
@@ -3105,7 +3129,7 @@ @@ -3105,7 +3129,7 @@
3105 "likes": 0, 3129 "likes": 0,
3106 "createdAt": 1787588544667, 3130 "createdAt": 1787588544667,
3107 "promptEn": "white studio shoot, two models wearing futuristic glasses", 3131 "promptEn": "white studio shoot, two models wearing futuristic glasses",
3108 - "lastPlayedAt": 1788232326384 3132 + "lastPlayedAt": 1788239826056
3109 }, 3133 },
3110 { 3134 {
3111 "id": "71a4d093aebc", 3135 "id": "71a4d093aebc",
@@ -3119,7 +3143,8 @@ @@ -3119,7 +3143,8 @@
3119 "partition": "ref2va", 3143 "partition": "ref2va",
3120 "likes": 0, 3144 "likes": 0,
3121 "createdAt": 1787588544109, 3145 "createdAt": 1787588544109,
3122 - "promptEn": "vampire romance drama trailer, girl wanders into the castle" 3146 + "promptEn": "vampire romance drama trailer, girl wanders into the castle",
  3147 + "lastPlayedAt": 1788234595640
3123 }, 3148 },
3124 { 3149 {
3125 "id": "7ded7db676d3", 3150 "id": "7ded7db676d3",
@@ -3134,7 +3159,7 @@ @@ -3134,7 +3159,7 @@
3134 "likes": 0, 3159 "likes": 0,
3135 "createdAt": 1787588543551, 3160 "createdAt": 1787588543551,
3136 "promptEn": "MiniMax H3 tech brand film, blue waveform logo", 3161 "promptEn": "MiniMax H3 tech brand film, blue waveform logo",
3137 - "lastPlayedAt": 1788232834747 3162 + "lastPlayedAt": 1788237714306
3138 }, 3163 },
3139 { 3164 {
3140 "id": "81711242178d", 3165 "id": "81711242178d",
@@ -3149,7 +3174,7 @@ @@ -3149,7 +3174,7 @@
3149 "likes": 0, 3174 "likes": 0,
3150 "createdAt": 1787588542968, 3175 "createdAt": 1787588542968,
3151 "promptEn": "mystery crime title sequence, collage split-screen credits", 3176 "promptEn": "mystery crime title sequence, collage split-screen credits",
3152 - "lastPlayedAt": 1788232113749 3177 + "lastPlayedAt": 1788239768008
3153 }, 3178 },
3154 { 3179 {
3155 "id": "67ababbea78f", 3180 "id": "67ababbea78f",
@@ -3164,7 +3189,7 @@ @@ -3164,7 +3189,7 @@
3164 "likes": 0, 3189 "likes": 0,
3165 "createdAt": 1787588542426, 3190 "createdAt": 1787588542426,
3166 "promptEn": "fashion guy in sunglasses and coat by a fire", 3191 "promptEn": "fashion guy in sunglasses and coat by a fire",
3167 - "lastPlayedAt": 1788233623102 3192 + "lastPlayedAt": 1788239534502
3168 }, 3193 },
3169 { 3194 {
3170 "id": "c1237b599bf4", 3195 "id": "c1237b599bf4",
@@ -3209,7 +3234,7 @@ @@ -3209,7 +3234,7 @@
3209 "likes": 0, 3234 "likes": 0,
3210 "createdAt": 1787588540642, 3235 "createdAt": 1787588540642,
3211 "promptEn": "telescope view searching for a MiniMax device, fashionable passerby", 3236 "promptEn": "telescope view searching for a MiniMax device, fashionable passerby",
3212 - "lastPlayedAt": 1788232740670 3237 + "lastPlayedAt": 1788238735151
3213 }, 3238 },
3214 { 3239 {
3215 "id": "a59cae01ec39", 3240 "id": "a59cae01ec39",
@@ -3224,7 +3249,7 @@ @@ -3224,7 +3249,7 @@
3224 "likes": 0, 3249 "likes": 0,
3225 "createdAt": 1787588540567, 3250 "createdAt": 1787588540567,
3226 "promptEn": "telescope view searching for a MiniMax device", 3251 "promptEn": "telescope view searching for a MiniMax device",
3227 - "lastPlayedAt": 1788231329035 3252 + "lastPlayedAt": 1788238332809
3228 }, 3253 },
3229 { 3254 {
3230 "id": "ba8c0758e5b4", 3255 "id": "ba8c0758e5b4",
@@ -3239,7 +3264,7 @@ @@ -3239,7 +3264,7 @@
3239 "likes": 0, 3264 "likes": 0,
3240 "createdAt": 1787588114381, 3265 "createdAt": 1787588114381,
3241 "promptEn": "underground rap duo freestyling at the camera", 3266 "promptEn": "underground rap duo freestyling at the camera",
3242 - "lastPlayedAt": 1788232182310 3267 + "lastPlayedAt": 1788238348824
3243 }, 3268 },
3244 { 3269 {
3245 "id": "58a439a93c02", 3270 "id": "58a439a93c02",
@@ -3254,7 +3279,7 @@ @@ -3254,7 +3279,7 @@
3254 "likes": 0, 3279 "likes": 0,
3255 "createdAt": 1787577878370, 3280 "createdAt": 1787577878370,
3256 "promptEn": "man barges into the old house apologizing, woman in white behind the screen", 3281 "promptEn": "man barges into the old house apologizing, woman in white behind the screen",
3257 - "lastPlayedAt": 1788232887827 3282 + "lastPlayedAt": 1788237932471
3258 }, 3283 },
3259 { 3284 {
3260 "id": "0e75ef71c3d0", 3285 "id": "0e75ef71c3d0",
@@ -3269,7 +3294,7 @@ @@ -3269,7 +3294,7 @@
3269 "likes": 0, 3294 "likes": 0,
3270 "createdAt": 1787577088842, 3295 "createdAt": 1787577088842,
3271 "promptEn": "man thanks the woman for taking him in, behind the screen", 3296 "promptEn": "man thanks the woman for taking him in, behind the screen",
3272 - "lastPlayedAt": 1788232866775 3297 + "lastPlayedAt": 1788235894800
3273 }, 3298 },
3274 { 3299 {
3275 "id": "e656777b6eef", 3300 "id": "e656777b6eef",
@@ -3283,7 +3308,8 @@ @@ -3283,7 +3308,8 @@
3283 "partition": "text", 3308 "partition": "text",
3284 "likes": 0, 3309 "likes": 0,
3285 "createdAt": 1787577088778, 3310 "createdAt": 1787577088778,
3286 - "promptEn": "man thanks the woman for taking him in" 3311 + "promptEn": "man thanks the woman for taking him in",
  3312 + "lastPlayedAt": 1788239552505
3287 }, 3313 },
3288 { 3314 {
3289 "id": "8d0270466c35", 3315 "id": "8d0270466c35",
@@ -3298,7 +3324,7 @@ @@ -3298,7 +3324,7 @@
3298 "likes": 0, 3324 "likes": 0,
3299 "createdAt": 1787576943673, 3325 "createdAt": 1787576943673,
3300 "promptEn": "man lowers his voice like she said, checks the door, window and lamp", 3326 "promptEn": "man lowers his voice like she said, checks the door, window and lamp",
3301 - "lastPlayedAt": 1788232408430 3327 + "lastPlayedAt": 1788239307486
3302 }, 3328 },
3303 { 3329 {
3304 "id": "396de15c1439", 3330 "id": "396de15c1439",
@@ -3313,7 +3339,7 @@ @@ -3313,7 +3339,7 @@
3313 "likes": 0, 3339 "likes": 0,
3314 "createdAt": 1787576943608, 3340 "createdAt": 1787576943608,
3315 "promptEn": "man lowers his voice like she said, checks the door, window and lamp", 3341 "promptEn": "man lowers his voice like she said, checks the door, window and lamp",
3316 - "lastPlayedAt": 1788233675127 3342 + "lastPlayedAt": 1788237526211
3317 }, 3343 },
3318 { 3344 {
3319 "id": "cde82791f726", 3345 "id": "cde82791f726",
@@ -3328,7 +3354,7 @@ @@ -3328,7 +3354,7 @@
3328 "likes": 0, 3354 "likes": 0,
3329 "createdAt": 1787576798050, 3355 "createdAt": 1787576798050,
3330 "promptEn": "man says he's willing to give her his heart", 3356 "promptEn": "man says he's willing to give her his heart",
3331 - "lastPlayedAt": 1788231829543 3357 + "lastPlayedAt": 1788238452913
3332 }, 3358 },
3333 { 3359 {
3334 "id": "57f2c6fa351d", 3360 "id": "57f2c6fa351d",
@@ -3342,7 +3368,8 @@ @@ -3342,7 +3368,8 @@
3342 "partition": "text", 3368 "partition": "text",
3343 "likes": 0, 3369 "likes": 0,
3344 "createdAt": 1787576651668, 3370 "createdAt": 1787576651668,
3345 - "promptEn": "man silently looks at her, doesn't respond" 3371 + "promptEn": "man silently looks at her, doesn't respond",
  3372 + "lastPlayedAt": 1788238781189
3346 }, 3373 },
3347 { 3374 {
3348 "id": "5958b86ab8f4", 3375 "id": "5958b86ab8f4",
@@ -3356,7 +3383,7 @@ @@ -3356,7 +3383,7 @@
3356 "partition": "text", 3383 "partition": "text",
3357 "likes": 0, 3384 "likes": 0,
3358 "createdAt": 1787576506447, 3385 "createdAt": 1787576506447,
3359 - "lastPlayedAt": 1788232658608, 3386 + "lastPlayedAt": 1788239732650,
3360 "promptEn": "man does as she says but stays on guard" 3387 "promptEn": "man does as she says but stays on guard"
3361 }, 3388 },
3362 { 3389 {
@@ -3371,7 +3398,7 @@ @@ -3371,7 +3398,7 @@
3371 "partition": "text", 3398 "partition": "text",
3372 "likes": 0, 3399 "likes": 0,
3373 "createdAt": 1787576358982, 3400 "createdAt": 1787576358982,
3374 - "lastPlayedAt": 1788230984868, 3401 + "lastPlayedAt": 1788238965299,
3375 "promptEn": "man apologizes, says something outside was chasing him" 3402 "promptEn": "man apologizes, says something outside was chasing him"
3376 }, 3403 },
3377 { 3404 {
@@ -3387,7 +3414,7 @@ @@ -3387,7 +3414,7 @@
3387 "likes": 0, 3414 "likes": 0,
3388 "createdAt": 1787576013870, 3415 "createdAt": 1787576013870,
3389 "promptEn": "man apologizes, says something outside was chasing him", 3416 "promptEn": "man apologizes, says something outside was chasing him",
3390 - "lastPlayedAt": 1788232670613 3417 + "lastPlayedAt": 1788238583032
3391 }, 3418 },
3392 { 3419 {
3393 "id": "7d425d2d35fb", 3420 "id": "7d425d2d35fb",
@@ -3402,7 +3429,7 @@ @@ -3402,7 +3429,7 @@
3402 "likes": 0, 3429 "likes": 0,
3403 "createdAt": 1787575790848, 3430 "createdAt": 1787575790848,
3404 "promptEn": "man apologizes, says something outside was chasing him", 3431 "promptEn": "man apologizes, says something outside was chasing him",
3405 - "lastPlayedAt": 1788232526503 3432 + "lastPlayedAt": 1788238955291
3406 }, 3433 },
3407 { 3434 {
3408 "id": "5e6dcf2c8cfd", 3435 "id": "5e6dcf2c8cfd",
@@ -3417,7 +3444,7 @@ @@ -3417,7 +3444,7 @@
3417 "likes": 0, 3444 "likes": 0,
3418 "createdAt": 1787575164928, 3445 "createdAt": 1787575164928,
3419 "promptEn": "man apologizes, says something outside was chasing him", 3446 "promptEn": "man apologizes, says something outside was chasing him",
3420 - "lastPlayedAt": 1788233417016 3447 + "lastPlayedAt": 1788237376095
3421 }, 3448 },
3422 { 3449 {
3423 "id": "250fad90393c", 3450 "id": "250fad90393c",
@@ -3432,7 +3459,7 @@ @@ -3432,7 +3459,7 @@
3432 "likes": 0, 3459 "likes": 0,
3433 "createdAt": 1787575164766, 3460 "createdAt": 1787575164766,
3434 "promptEn": "man apologizes, says something outside was chasing him", 3461 "promptEn": "man apologizes, says something outside was chasing him",
3435 - "lastPlayedAt": 1788233171823 3462 + "lastPlayedAt": 1788239027329
3436 }, 3463 },
3437 { 3464 {
3438 "id": "fb3000701384", 3465 "id": "fb3000701384",
@@ -3447,7 +3474,7 @@ @@ -3447,7 +3474,7 @@
3447 "likes": 0, 3474 "likes": 0,
3448 "createdAt": 1787573698485, 3475 "createdAt": 1787573698485,
3449 "promptEn": "underground rap duo freestyling at the camera", 3476 "promptEn": "underground rap duo freestyling at the camera",
3450 - "lastPlayedAt": 1788231445130 3477 + "lastPlayedAt": 1788235852763
3451 }, 3478 },
3452 { 3479 {
3453 "id": "e5246eae814e", 3480 "id": "e5246eae814e",
@@ -3462,7 +3489,7 @@ @@ -3462,7 +3489,7 @@
3462 "likes": 0, 3489 "likes": 0,
3463 "createdAt": 1787569314178, 3490 "createdAt": 1787569314178,
3464 "promptEn": "man apologizes, says something outside was chasing him", 3491 "promptEn": "man apologizes, says something outside was chasing him",
3465 - "lastPlayedAt": 1788232479825 3492 + "lastPlayedAt": 1788238593040
3466 }, 3493 },
3467 { 3494 {
3468 "id": "a0d7a106b8d6", 3495 "id": "a0d7a106b8d6",
@@ -3476,7 +3503,7 @@ @@ -3476,7 +3503,7 @@
3476 "partition": "text", 3503 "partition": "text",
3477 "likes": 0, 3504 "likes": 0,
3478 "createdAt": 1787568512602, 3505 "createdAt": 1787568512602,
3479 - "lastPlayedAt": 1788233062862, 3506 + "lastPlayedAt": 1788238088593,
3480 "promptEn": "man apologizes, says something outside was chasing him" 3507 "promptEn": "man apologizes, says something outside was chasing him"
3481 }, 3508 },
3482 { 3509 {
@@ -3491,7 +3518,7 @@ @@ -3491,7 +3518,7 @@
3491 "partition": "ref2va", 3518 "partition": "ref2va",
3492 "likes": 0, 3519 "likes": 0,
3493 "createdAt": 1787559156410, 3520 "createdAt": 1787559156410,
3494 - "lastPlayedAt": 1788231162964, 3521 + "lastPlayedAt": 1788237187923,
3495 "promptEn": "two characters sitting, game menu UI swaps in a mechanical arm" 3522 "promptEn": "two characters sitting, game menu UI swaps in a mechanical arm"
3496 }, 3523 },
3497 { 3524 {
@@ -3506,7 +3533,8 @@ @@ -3506,7 +3533,8 @@
3506 "partition": "ref2va", 3533 "partition": "ref2va",
3507 "likes": 0, 3534 "likes": 0,
3508 "createdAt": 1787542770522, 3535 "createdAt": 1787542770522,
3509 - "promptEn": "clip cut to match a reference video's editing rhythm" 3536 + "promptEn": "clip cut to match a reference video's editing rhythm",
  3537 + "lastPlayedAt": 1788239790028
3510 }, 3538 },
3511 { 3539 {
3512 "id": "f9286d694bad", 3540 "id": "f9286d694bad",
@@ -3521,7 +3549,7 @@ @@ -3521,7 +3549,7 @@
3521 "likes": 0, 3549 "likes": 0,
3522 "createdAt": 1787497396594, 3550 "createdAt": 1787497396594,
3523 "promptEn": "otome game male lead showcase PV", 3551 "promptEn": "otome game male lead showcase PV",
3524 - "lastPlayedAt": 1788232490471 3552 + "lastPlayedAt": 1788237590243
3525 }, 3553 },
3526 { 3554 {
3527 "id": "36ac12f2f29e", 3555 "id": "36ac12f2f29e",
@@ -3535,7 +3563,8 @@ @@ -3535,7 +3563,8 @@
3535 "partition": "ref2va", 3563 "partition": "ref2va",
3536 "likes": 0, 3564 "likes": 0,
3537 "createdAt": 1787493339415, 3565 "createdAt": 1787493339415,
3538 - "promptEn": "game menu UI, character swaps out a mechanical arm" 3566 + "promptEn": "game menu UI, character swaps out a mechanical arm",
  3567 + "lastPlayedAt": 1788239837605
3539 }, 3568 },
3540 { 3569 {
3541 "id": "656c897b04fc", 3570 "id": "656c897b04fc",
@@ -3549,7 +3578,8 @@ @@ -3549,7 +3578,8 @@
3549 "partition": "ref2va", 3578 "partition": "ref2va",
3550 "likes": 0, 3579 "likes": 0,
3551 "createdAt": 1787493303602, 3580 "createdAt": 1787493303602,
3552 - "promptEn": "game menu UI, character swaps out a mechanical arm" 3581 + "promptEn": "game menu UI, character swaps out a mechanical arm",
  3582 + "lastPlayedAt": 1788238364841
3553 }, 3583 },
3554 { 3584 {
3555 "id": "e1fd99e4ab97", 3585 "id": "e1fd99e4ab97",
@@ -3564,7 +3594,7 @@ @@ -3564,7 +3594,7 @@
3564 "likes": 0, 3594 "likes": 0,
3565 "createdAt": 1787470101960, 3595 "createdAt": 1787470101960,
3566 "promptEn": "cute hand-drawn beagle dog making faces, game screen", 3596 "promptEn": "cute hand-drawn beagle dog making faces, game screen",
3567 - "lastPlayedAt": 1788232202318 3597 + "lastPlayedAt": 1788235590540
3568 }, 3598 },
3569 { 3599 {
3570 "id": "4067993ff824", 3600 "id": "4067993ff824",
@@ -3579,7 +3609,7 @@ @@ -3579,7 +3609,7 @@
3579 "likes": 0, 3609 "likes": 0,
3580 "createdAt": 1787470101693, 3610 "createdAt": 1787470101693,
3581 "promptEn": "black-background product-launch style shot of a roast duck", 3611 "promptEn": "black-background product-launch style shot of a roast duck",
3582 - "lastPlayedAt": 1788233499050 3612 + "lastPlayedAt": 1788239608539
3583 }, 3613 },
3584 { 3614 {
3585 "id": "99e771bbb50d", 3615 "id": "99e771bbb50d",
@@ -3608,7 +3638,7 @@ @@ -3608,7 +3638,7 @@
3608 "likes": 0, 3638 "likes": 0,
3609 "createdAt": 1787470101060, 3639 "createdAt": 1787470101060,
3610 "promptEn": "snowy bamboo forest at night, wuxia mystery standoff", 3640 "promptEn": "snowy bamboo forest at night, wuxia mystery standoff",
3611 - "lastPlayedAt": 1788231499181 3641 + "lastPlayedAt": 1788238691117
3612 }, 3642 },
3613 { 3643 {
3614 "id": "125751cac1c8", 3644 "id": "125751cac1c8",
@@ -3623,7 +3653,7 @@ @@ -3623,7 +3653,7 @@
3623 "likes": 0, 3653 "likes": 0,
3624 "createdAt": 1787470100377, 3654 "createdAt": 1787470100377,
3625 "promptEn": "all-black background, experimental Chinese kinetic typography", 3655 "promptEn": "all-black background, experimental Chinese kinetic typography",
3626 - "lastPlayedAt": 1788233176921 3656 + "lastPlayedAt": 1788239704619
3627 }, 3657 },
3628 { 3658 {
3629 "id": "c24c15736daa", 3659 "id": "c24c15736daa",
@@ -3638,7 +3668,7 @@ @@ -3638,7 +3668,7 @@
3638 "likes": 0, 3668 "likes": 0,
3639 "createdAt": 1787470100107, 3669 "createdAt": 1787470100107,
3640 "promptEn": "MiniMax H3 brand motion graphics, a dot growing into a network", 3670 "promptEn": "MiniMax H3 brand motion graphics, a dot growing into a network",
3641 - "lastPlayedAt": 1788233154911 3671 + "lastPlayedAt": 1788235878787
3642 }, 3672 },
3643 { 3673 {
3644 "id": "194e9bd77da6", 3674 "id": "194e9bd77da6",
@@ -3653,7 +3683,7 @@ @@ -3653,7 +3683,7 @@
3653 "likes": 0, 3683 "likes": 0,
3654 "createdAt": 1787470100022, 3684 "createdAt": 1787470100022,
3655 "promptEn": "MiniMax H3 brand motion graphics, a dot growing into a network", 3685 "promptEn": "MiniMax H3 brand motion graphics, a dot growing into a network",
3656 - "lastPlayedAt": 1788233112891 3686 + "lastPlayedAt": 1788239517585
3657 }, 3687 },
3658 { 3688 {
3659 "id": "eaf2a1433c41", 3689 "id": "eaf2a1433c41",
@@ -3668,7 +3698,7 @@ @@ -3668,7 +3698,7 @@
3668 "likes": 0, 3698 "likes": 0,
3669 "createdAt": 1787470099635, 3699 "createdAt": 1787470099635,
3670 "promptEn": "pointing at things at a beach market, they turn into hand-drawn animation", 3700 "promptEn": "pointing at things at a beach market, they turn into hand-drawn animation",
3671 - "lastPlayedAt": 1788233100885 3701 + "lastPlayedAt": 1788239313488
3672 }, 3702 },
3673 { 3703 {
3674 "id": "0fe0f2101c44", 3704 "id": "0fe0f2101c44",
@@ -3682,7 +3712,8 @@ @@ -3682,7 +3712,8 @@
3682 "partition": "text", 3712 "partition": "text",
3683 "likes": 0, 3713 "likes": 0,
3684 "createdAt": 1787470099306, 3714 "createdAt": 1787470099306,
3685 - "promptEn": "late-night laundromat footage mixed with glowing hand-drawn animation" 3715 + "promptEn": "late-night laundromat footage mixed with glowing hand-drawn animation",
  3716 + "lastPlayedAt": 1788237966499
3686 }, 3717 },
3687 { 3718 {
3688 "id": "c0e23beb4132", 3719 "id": "c0e23beb4132",
@@ -3697,7 +3728,7 @@ @@ -3697,7 +3728,7 @@
3697 "likes": 0, 3728 "likes": 0,
3698 "createdAt": 1787470098914, 3729 "createdAt": 1787470098914,
3699 "promptEn": "evening kitchen footage mixed with a glowing hand-drawn creature", 3730 "promptEn": "evening kitchen footage mixed with a glowing hand-drawn creature",
3700 - "lastPlayedAt": 1788232278363 3731 + "lastPlayedAt": 1788236951715
3701 }, 3732 },
3702 { 3733 {
3703 "id": "6639b07acc7b", 3734 "id": "6639b07acc7b",
@@ -3727,7 +3758,7 @@ @@ -3727,7 +3758,7 @@
3727 "likes": 0, 3758 "likes": 0,
3728 "createdAt": 1787461994394, 3759 "createdAt": 1787461994394,
3729 "promptEn": "a quiet mountain village at dawn (second attempt)", 3760 "promptEn": "a quiet mountain village at dawn (second attempt)",
3730 - "lastPlayedAt": 1788232130282 3761 + "lastPlayedAt": 1788238893265
3731 }, 3762 },
3732 { 3763 {
3733 "id": "8e16b6faafd9", 3764 "id": "8e16b6faafd9",
@@ -3742,7 +3773,7 @@ @@ -3742,7 +3773,7 @@
3742 "likes": 0, 3773 "likes": 0,
3743 "createdAt": 1787455895150, 3774 "createdAt": 1787455895150,
3744 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3775 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3745 - "lastPlayedAt": 1788231515190 3776 + "lastPlayedAt": 1788238458919
3746 }, 3777 },
3747 { 3778 {
3748 "id": "2a4e45820135", 3779 "id": "2a4e45820135",
@@ -3757,7 +3788,7 @@ @@ -3757,7 +3788,7 @@
3757 "likes": 0, 3788 "likes": 0,
3758 "createdAt": 1787453697093, 3789 "createdAt": 1787453697093,
3759 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3790 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3760 - "lastPlayedAt": 1788233511073 3791 + "lastPlayedAt": 1788238106611
3761 }, 3792 },
3762 { 3793 {
3763 "id": "bfe13d1cc4a2", 3794 "id": "bfe13d1cc4a2",
@@ -3771,7 +3802,8 @@ @@ -3771,7 +3802,8 @@
3771 "partition": "text", 3802 "partition": "text",
3772 "likes": 0, 3803 "likes": 0,
3773 "createdAt": 1787452521817, 3804 "createdAt": 1787452521817,
3774 - "promptEn": "protagonist leaps across rooftops fleeing pursuers" 3805 + "promptEn": "protagonist leaps across rooftops fleeing pursuers",
  3806 + "lastPlayedAt": 1788238250741
3775 }, 3807 },
3776 { 3808 {
3777 "id": "f36cdd73ff1b", 3809 "id": "f36cdd73ff1b",
@@ -3786,7 +3818,7 @@ @@ -3786,7 +3818,7 @@
3786 "likes": 0, 3818 "likes": 0,
3787 "createdAt": 1787450754171, 3819 "createdAt": 1787450754171,
3788 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3820 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3789 - "lastPlayedAt": 1788232430442 3821 + "lastPlayedAt": 1788237818368
3790 }, 3822 },
3791 { 3823 {
3792 "id": "fd0183c54763", 3824 "id": "fd0183c54763",
@@ -3800,7 +3832,8 @@ @@ -3800,7 +3832,8 @@
3800 "partition": "text", 3832 "partition": "text",
3801 "likes": 0, 3833 "likes": 0,
3802 "createdAt": 1787447175021, 3834 "createdAt": 1787447175021,
3803 - "promptEn": "protagonist leaps across rooftops fleeing pursuers" 3835 + "promptEn": "protagonist leaps across rooftops fleeing pursuers",
  3836 + "lastPlayedAt": 1788238641079
3804 }, 3837 },
3805 { 3838 {
3806 "id": "4580ca66725a", 3839 "id": "4580ca66725a",
@@ -3815,7 +3848,7 @@ @@ -3815,7 +3848,7 @@
3815 "likes": 0, 3848 "likes": 0,
3816 "createdAt": 1787444313690, 3849 "createdAt": 1787444313690,
3817 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3850 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3818 - "lastPlayedAt": 1788232555823 3851 + "lastPlayedAt": 1788239810044
3819 }, 3852 },
3820 { 3853 {
3821 "id": "e56962bd770f", 3854 "id": "e56962bd770f",
@@ -3830,7 +3863,7 @@ @@ -3830,7 +3863,7 @@
3830 "likes": 0, 3863 "likes": 0,
3831 "createdAt": 1787442174982, 3864 "createdAt": 1787442174982,
3832 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3865 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3833 - "lastPlayedAt": 1788233641108 3866 + "lastPlayedAt": 1788237380102
3834 }, 3867 },
3835 { 3868 {
3836 "id": "18105bc2900b", 3869 "id": "18105bc2900b",
@@ -3845,7 +3878,7 @@ @@ -3845,7 +3878,7 @@
3845 "likes": 0, 3878 "likes": 0,
3846 "createdAt": 1787437988124, 3879 "createdAt": 1787437988124,
3847 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3880 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3848 - "lastPlayedAt": 1788233437022 3881 + "lastPlayedAt": 1788238979307
3849 }, 3882 },
3850 { 3883 {
3851 "id": "e497093c79ce", 3884 "id": "e497093c79ce",
@@ -3860,7 +3893,7 @@ @@ -3860,7 +3893,7 @@
3860 "likes": 0, 3893 "likes": 0,
3861 "createdAt": 1787436029475, 3894 "createdAt": 1787436029475,
3862 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3895 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3863 - "lastPlayedAt": 1788233236940 3896 + "lastPlayedAt": 1788234989999
3864 }, 3897 },
3865 { 3898 {
3866 "id": "896859e59dae", 3899 "id": "896859e59dae",
@@ -3875,7 +3908,7 @@ @@ -3875,7 +3908,7 @@
3875 "likes": 0, 3908 "likes": 0,
3876 "createdAt": 1787432295531, 3909 "createdAt": 1787432295531,
3877 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3910 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3878 - "lastPlayedAt": 1788232582552 3911 + "lastPlayedAt": 1788236229080
3879 }, 3912 },
3880 { 3913 {
3881 "id": "79368c9cf499", 3914 "id": "79368c9cf499",
@@ -3890,7 +3923,7 @@ @@ -3890,7 +3923,7 @@
3890 "likes": 0, 3923 "likes": 0,
3891 "createdAt": 1787428860940, 3924 "createdAt": 1787428860940,
3892 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3925 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3893 - "lastPlayedAt": 1788232711826 3926 + "lastPlayedAt": 1788236855644
3894 }, 3927 },
3895 { 3928 {
3896 "id": "dbc7c27b72ba", 3929 "id": "dbc7c27b72ba",
@@ -3905,7 +3938,7 @@ @@ -3905,7 +3938,7 @@
3905 "likes": 0, 3938 "likes": 0,
3906 "createdAt": 1787426955312, 3939 "createdAt": 1787426955312,
3907 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3940 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3908 - "lastPlayedAt": 1788232996834 3941 + "lastPlayedAt": 1788236901679
3909 }, 3942 },
3910 { 3943 {
3911 "id": "734d28c52670", 3944 "id": "734d28c52670",
@@ -3920,7 +3953,7 @@ @@ -3920,7 +3953,7 @@
3920 "likes": 0, 3953 "likes": 0,
3921 "createdAt": 1787426484939, 3954 "createdAt": 1787426484939,
3922 "promptEn": "small boat sailing through a misty forest river", 3955 "promptEn": "small boat sailing through a misty forest river",
3923 - "lastPlayedAt": 1788232264353 3956 + "lastPlayedAt": 1788236929703
3924 }, 3957 },
3925 { 3958 {
3926 "id": "9fbcf0d87021", 3959 "id": "9fbcf0d87021",
@@ -3935,7 +3968,7 @@ @@ -3935,7 +3968,7 @@
3935 "likes": 0, 3968 "likes": 0,
3936 "createdAt": 1787425932119, 3969 "createdAt": 1787425932119,
3937 "promptEn": "lighthouse on a cliff at sunset, seagulls flying", 3970 "promptEn": "lighthouse on a cliff at sunset, seagulls flying",
3938 - "lastPlayedAt": 1788231891592 3971 + "lastPlayedAt": 1788239634566
3939 }, 3972 },
3940 { 3973 {
3941 "id": "cf2015093ec4", 3974 "id": "cf2015093ec4",
@@ -3950,7 +3983,7 @@ @@ -3950,7 +3983,7 @@
3950 "likes": 0, 3983 "likes": 0,
3951 "createdAt": 1787415067615, 3984 "createdAt": 1787415067615,
3952 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 3985 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3953 - "lastPlayedAt": 1788233819198 3986 + "lastPlayedAt": 1788239461561
3954 }, 3987 },
3955 { 3988 {
3956 "id": "9bcff387cddf", 3989 "id": "9bcff387cddf",
@@ -3965,7 +3998,7 @@ @@ -3965,7 +3998,7 @@
3965 "likes": 0, 3998 "likes": 0,
3966 "createdAt": 1787413999418, 3999 "createdAt": 1787413999418,
3967 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 4000 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3968 - "lastPlayedAt": 1788232955824 4001 + "lastPlayedAt": 1788239201420
3969 }, 4002 },
3970 { 4003 {
3971 "id": "ade0318a7a4a", 4004 "id": "ade0318a7a4a",
@@ -3980,7 +4013,7 @@ @@ -3980,7 +4013,7 @@
3980 "likes": 0, 4013 "likes": 0,
3981 "createdAt": 1787413511959, 4014 "createdAt": 1787413511959,
3982 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 4015 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3983 - "lastPlayedAt": 1788233218933 4016 + "lastPlayedAt": 1788237392113
3984 }, 4017 },
3985 { 4018 {
3986 "id": "5d4f40bf0889", 4019 "id": "5d4f40bf0889",
@@ -3994,7 +4027,8 @@ @@ -3994,7 +4027,8 @@
3994 "partition": "text", 4027 "partition": "text",
3995 "likes": 0, 4028 "likes": 0,
3996 "createdAt": 1787412952086, 4029 "createdAt": 1787412952086,
3997 - "promptEn": "protagonist leaps across rooftops fleeing pursuers" 4030 + "promptEn": "protagonist leaps across rooftops fleeing pursuers",
  4031 + "lastPlayedAt": 1788238436902
3998 }, 4032 },
3999 { 4033 {
4000 "id": "59fd7a087f5b", 4034 "id": "59fd7a087f5b",
@@ -4008,7 +4042,8 @@ @@ -4008,7 +4042,8 @@
4008 "partition": "text", 4042 "partition": "text",
4009 "likes": 0, 4043 "likes": 0,
4010 "createdAt": 1787412606465, 4044 "createdAt": 1787412606465,
4011 - "promptEn": "protagonist leaps across rooftops fleeing pursuers" 4045 + "promptEn": "protagonist leaps across rooftops fleeing pursuers",
  4046 + "lastPlayedAt": 1788238819228
4012 }, 4047 },
4013 { 4048 {
4014 "id": "29a99dc412b2", 4049 "id": "29a99dc412b2",
@@ -4022,7 +4057,8 @@ @@ -4022,7 +4057,8 @@
4022 "partition": "text", 4057 "partition": "text",
4023 "likes": 0, 4058 "likes": 0,
4024 "createdAt": 1787412150062, 4059 "createdAt": 1787412150062,
4025 - "promptEn": "protagonist leaps across rooftops fleeing pursuers" 4060 + "promptEn": "protagonist leaps across rooftops fleeing pursuers",
  4061 + "lastPlayedAt": 1788239387526
4026 }, 4062 },
4027 { 4063 {
4028 "id": "9d2486d6f969", 4064 "id": "9d2486d6f969",
@@ -4036,7 +4072,8 @@ @@ -4036,7 +4072,8 @@
4036 "partition": "text", 4072 "partition": "text",
4037 "likes": 0, 4073 "likes": 0,
4038 "createdAt": 1787397450973, 4074 "createdAt": 1787397450973,
4039 - "promptEn": "a cat sitting on a windowsill" 4075 + "promptEn": "a cat sitting on a windowsill",
  4076 + "lastPlayedAt": 1788235350315
4040 }, 4077 },
4041 { 4078 {
4042 "id": "580d1d722adc", 4079 "id": "580d1d722adc",
@@ -4051,7 +4088,7 @@ @@ -4051,7 +4088,7 @@
4051 "likes": 0, 4088 "likes": 0,
4052 "createdAt": 1787388090646, 4089 "createdAt": 1787388090646,
4053 "promptEn": "a cat sitting on a windowsill", 4090 "promptEn": "a cat sitting on a windowsill",
4054 - "lastPlayedAt": 1788233511058 4091 + "lastPlayedAt": 1788235956854
4055 }, 4092 },
4056 { 4093 {
4057 "id": "c551457bc55e", 4094 "id": "c551457bc55e",
@@ -4065,7 +4102,7 @@ @@ -4065,7 +4102,7 @@
4065 "partition": "text", 4102 "partition": "text",
4066 "likes": 0, 4103 "likes": 0,
4067 "createdAt": 1787386316175, 4104 "createdAt": 1787386316175,
4068 - "lastPlayedAt": 1788232954819, 4105 + "lastPlayedAt": 1788239634555,
4069 "promptEn": "a cat sitting on a windowsill" 4106 "promptEn": "a cat sitting on a windowsill"
4070 }, 4107 },
4071 { 4108 {
@@ -4081,7 +4118,7 @@ @@ -4081,7 +4118,7 @@
4081 "likes": 0, 4118 "likes": 0,
4082 "createdAt": 1787384983124, 4119 "createdAt": 1787384983124,
4083 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 4120 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4084 - "lastPlayedAt": 1788233687131 4121 + "lastPlayedAt": 1788239576512
4085 }, 4122 },
4086 { 4123 {
4087 "id": "7753a046ba79", 4124 "id": "7753a046ba79",
@@ -4096,7 +4133,7 @@ @@ -4096,7 +4133,7 @@
4096 "likes": 0, 4133 "likes": 0,
4097 "createdAt": 1787384417941, 4134 "createdAt": 1787384417941,
4098 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 4135 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4099 - "lastPlayedAt": 1788231933631 4136 + "lastPlayedAt": 1788239696610
4100 }, 4137 },
4101 { 4138 {
4102 "id": "2cf5c8e67510", 4139 "id": "2cf5c8e67510",
@@ -4111,7 +4148,7 @@ @@ -4111,7 +4148,7 @@
4111 "likes": 0, 4148 "likes": 0,
4112 "createdAt": 1787383911831, 4149 "createdAt": 1787383911831,
4113 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 4150 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4114 - "lastPlayedAt": 1788232706647 4151 + "lastPlayedAt": 1788239227431
4115 }, 4152 },
4116 { 4153 {
4117 "id": "9cba879e263d", 4154 "id": "9cba879e263d",
@@ -4126,7 +4163,7 @@ @@ -4126,7 +4163,7 @@
4126 "likes": 0, 4163 "likes": 0,
4127 "createdAt": 1787375413935, 4164 "createdAt": 1787375413935,
4128 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 4165 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4129 - "lastPlayedAt": 1788233360994 4166 + "lastPlayedAt": 1788239578514
4130 }, 4167 },
4131 { 4168 {
4132 "id": "10112fb3c1fd", 4169 "id": "10112fb3c1fd",
@@ -4140,7 +4177,8 @@ @@ -4140,7 +4177,8 @@
4140 "partition": "fl2va", 4177 "partition": "fl2va",
4141 "likes": 0, 4178 "likes": 0,
4142 "createdAt": 1787375413623, 4179 "createdAt": 1787375413623,
4143 - "promptEn": "golden portal opens, man in green jacket throws a glowing spear" 4180 + "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
  4181 + "lastPlayedAt": 1788238094600
4144 }, 4182 },
4145 { 4183 {
4146 "id": "78e698c924b1", 4184 "id": "78e698c924b1",
@@ -4154,7 +4192,8 @@ @@ -4154,7 +4192,8 @@
4154 "partition": "fl2va", 4192 "partition": "fl2va",
4155 "likes": 0, 4193 "likes": 0,
4156 "createdAt": 1787375413203, 4194 "createdAt": 1787375413203,
4157 - "promptEn": "golden portal opens, man in green jacket throws a glowing spear" 4195 + "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
  4196 + "lastPlayedAt": 1788239359509
4158 }, 4197 },
4159 { 4198 {
4160 "id": "219ed88c3ea7", 4199 "id": "219ed88c3ea7",
@@ -4169,7 +4208,7 @@ @@ -4169,7 +4208,7 @@
4169 "likes": 0, 4208 "likes": 0,
4170 "createdAt": 1787375412946, 4209 "createdAt": 1787375412946,
4171 "promptEn": "golden portal opens, man in green jacket throws a glowing spear", 4210 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4172 - "lastPlayedAt": 1788233815949 4211 + "lastPlayedAt": 1788238897263
4173 }, 4212 },
4174 { 4213 {
4175 "id": "a72236902363", 4214 "id": "a72236902363",
@@ -4184,7 +4223,7 @@ @@ -4184,7 +4223,7 @@
4184 "likes": 0, 4223 "likes": 0,
4185 "createdAt": 1787367454249, 4224 "createdAt": 1787367454249,
4186 "promptEn": "golden portal opens, man in green jacket throws a glowing spear", 4225 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4187 - "lastPlayedAt": 1788232523825 4226 + "lastPlayedAt": 1788235826739
4188 }, 4227 },
4189 { 4228 {
4190 "id": "c48af2f34e62", 4229 "id": "c48af2f34e62",
@@ -4198,7 +4237,8 @@ @@ -4198,7 +4237,8 @@
4198 "partition": "fl2va", 4237 "partition": "fl2va",
4199 "likes": 0, 4238 "likes": 0,
4200 "createdAt": 1787367454065, 4239 "createdAt": 1787367454065,
4201 - "promptEn": "golden portal opens, man in green jacket throws a glowing spear" 4240 + "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
  4241 + "lastPlayedAt": 1788238623063
4202 }, 4242 },
4203 { 4243 {
4204 "id": "e6c241be377f", 4244 "id": "e6c241be377f",
@@ -4213,7 +4253,7 @@ @@ -4213,7 +4253,7 @@
4213 "likes": 0, 4253 "likes": 0,
4214 "createdAt": 1787367453839, 4254 "createdAt": 1787367453839,
4215 "promptEn": "golden portal opens, man in green jacket throws a glowing spear", 4255 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4216 - "lastPlayedAt": 1788232667826 4256 + "lastPlayedAt": 1788239055341
4217 }, 4257 },
4218 { 4258 {
4219 "id": "8402adae59dc", 4259 "id": "8402adae59dc",
@@ -4228,7 +4268,7 @@ @@ -4228,7 +4268,7 @@
4228 "likes": 0, 4268 "likes": 0,
4229 "createdAt": 1787363956909, 4269 "createdAt": 1787363956909,
4230 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 4270 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4231 - "lastPlayedAt": 1788232803824 4271 + "lastPlayedAt": 1788238048564
4232 }, 4272 },
4233 { 4273 {
4234 "id": "5692965402f5", 4274 "id": "5692965402f5",
@@ -4243,7 +4283,7 @@ @@ -4243,7 +4283,7 @@
4243 "likes": 0, 4283 "likes": 0,
4244 "createdAt": 1787332996051, 4284 "createdAt": 1787332996051,
4245 "promptEn": "golden portal opens, man in green jacket throws a glowing spear", 4285 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4246 - "lastPlayedAt": 1788232624582 4286 + "lastPlayedAt": 1788238937288
4247 }, 4287 },
4248 { 4288 {
4249 "id": "bf7d973d02a3", 4289 "id": "bf7d973d02a3",
@@ -4258,7 +4298,7 @@ @@ -4258,7 +4298,7 @@
4258 "likes": 0, 4298 "likes": 0,
4259 "createdAt": 1787332047061, 4299 "createdAt": 1787332047061,
4260 "promptEn": "golden portal opens, man in green jacket throws a glowing spear", 4300 "promptEn": "golden portal opens, man in green jacket throws a glowing spear",
4261 - "lastPlayedAt": 1788231825536 4301 + "lastPlayedAt": 1788238663091
4262 }, 4302 },
4263 { 4303 {
4264 "id": "a5688d8f5db3", 4304 "id": "a5688d8f5db3",
@@ -4272,7 +4312,8 @@ @@ -4272,7 +4312,8 @@
4272 "partition": "fl2va", 4312 "partition": "fl2va",
4273 "likes": 0, 4313 "likes": 0,
4274 "createdAt": 1787330101793, 4314 "createdAt": 1787330101793,
4275 - "promptEn": "a red apple on a wooden table, cinematic lighting" 4315 + "promptEn": "a red apple on a wooden table, cinematic lighting",
  4316 + "lastPlayedAt": 1788239409539
4276 }, 4317 },
4277 { 4318 {
4278 "id": "0024d3c2d4cb", 4319 "id": "0024d3c2d4cb",
@@ -4302,7 +4343,7 @@ @@ -4302,7 +4343,7 @@
4302 "likes": 0, 4343 "likes": 0,
4303 "createdAt": 1787326019290, 4344 "createdAt": 1787326019290,
4304 "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella", 4345 "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella",
4305 - "lastPlayedAt": 1788232768693 4346 + "lastPlayedAt": 1788239121373
4306 }, 4347 },
4307 { 4348 {
4308 "id": "fd8534ce588c", 4349 "id": "fd8534ce588c",
@@ -4317,7 +4358,7 @@ @@ -4317,7 +4358,7 @@
4317 "likes": 0, 4358 "likes": 0,
4318 "createdAt": 1787309864037, 4359 "createdAt": 1787309864037,
4319 "promptEn": "transparent gaming mouse product film, blue and orange lighting", 4360 "promptEn": "transparent gaming mouse product film, blue and orange lighting",
4320 - "lastPlayedAt": 1788233134901 4361 + "lastPlayedAt": 1788237984509
4321 }, 4362 },
4322 { 4363 {
4323 "id": "073f5df6427a", 4364 "id": "073f5df6427a",
@@ -4331,7 +4372,8 @@ @@ -4331,7 +4372,8 @@
4331 "partition": "text", 4372 "partition": "text",
4332 "likes": 0, 4373 "likes": 0,
4333 "createdAt": 1787305544815, 4374 "createdAt": 1787305544815,
4334 - "promptEn": "transparent gaming mouse product film, blue and orange lighting" 4375 + "promptEn": "transparent gaming mouse product film, blue and orange lighting",
  4376 + "lastPlayedAt": 1788239497567
4335 }, 4377 },
4336 { 4378 {
4337 "id": "e23b04ac2c78", 4379 "id": "e23b04ac2c78",
@@ -4346,7 +4388,7 @@ @@ -4346,7 +4388,7 @@
4346 "likes": 0, 4388 "likes": 0,
4347 "createdAt": 1787304875681, 4389 "createdAt": 1787304875681,
4348 "promptEn": "protagonist leaps across rooftops fleeing pursuers", 4390 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4349 - "lastPlayedAt": 1788232911824 4391 + "lastPlayedAt": 1788239217427
4350 }, 4392 },
4351 { 4393 {
4352 "id": "c84bc1c7bfa0", 4394 "id": "c84bc1c7bfa0",
@@ -4360,7 +4402,8 @@ @@ -4360,7 +4402,8 @@
4360 "partition": "ref2va", 4402 "partition": "ref2va",
4361 "likes": 0, 4403 "likes": 0,
4362 "createdAt": 1787304031214, 4404 "createdAt": 1787304031214,
4363 - "promptEn": "constable asks how she's sure it's not human, she says a person would make a sound" 4405 + "promptEn": "constable asks how she's sure it's not human, she says a person would make a sound",
  4406 + "lastPlayedAt": 1788239229433
4364 }, 4407 },
4365 { 4408 {
4366 "id": "23d2fd81eb4d", 4409 "id": "23d2fd81eb4d",
@@ -4375,7 +4418,7 @@ @@ -4375,7 +4418,7 @@
4375 "likes": 0, 4418 "likes": 0,
4376 "createdAt": 1787304031103, 4419 "createdAt": 1787304031103,
4377 "promptEn": "constable asks how she's sure it's not human, she says a person would make a sound", 4420 "promptEn": "constable asks how she's sure it's not human, she says a person would make a sound",
4378 - "lastPlayedAt": 1788232754686 4421 + "lastPlayedAt": 1788239666580
4379 }, 4422 },
4380 { 4423 {
4381 "id": "807e042dce0d", 4424 "id": "807e042dce0d",
@@ -4389,7 +4432,8 @@ @@ -4389,7 +4432,8 @@
4389 "partition": "text", 4432 "partition": "text",
4390 "likes": 0, 4433 "likes": 0,
4391 "createdAt": 1787290119529, 4434 "createdAt": 1787290119529,
4392 - "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella" 4435 + "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella",
  4436 + "lastPlayedAt": 1788237290006
4393 }, 4437 },
4394 { 4438 {
4395 "id": "06bb43196c46", 4439 "id": "06bb43196c46",
@@ -4404,7 +4448,7 @@ @@ -4404,7 +4448,7 @@
4404 "likes": 0, 4448 "likes": 0,
4405 "createdAt": 1787290119114, 4449 "createdAt": 1787290119114,
4406 "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella", 4450 "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella",
4407 - "lastPlayedAt": 1788232126281 4451 + "lastPlayedAt": 1788238827234
4408 }, 4452 },
4409 { 4453 {
4410 "id": "0f22388f7847", 4454 "id": "0f22388f7847",
@@ -4418,7 +4462,8 @@ @@ -4418,7 +4462,8 @@
4418 "partition": "text", 4462 "partition": "text",
4419 "likes": 0, 4463 "likes": 0,
4420 "createdAt": 1787290118806, 4464 "createdAt": 1787290118806,
4421 - "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella" 4465 + "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella",
  4466 + "lastPlayedAt": 1788235388347
4422 }, 4467 },
4423 { 4468 {
4424 "id": "0c67d73b06d4", 4469 "id": "0c67d73b06d4",
@@ -4433,7 +4478,7 @@ @@ -4433,7 +4478,7 @@
4433 "likes": 0, 4478 "likes": 0,
4434 "createdAt": 1787231456079, 4479 "createdAt": 1787231456079,
4435 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own", 4480 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own",
4436 - "lastPlayedAt": 1788233411014 4481 + "lastPlayedAt": 1788237107861
4437 }, 4482 },
4438 { 4483 {
4439 "id": "66158f543a20", 4484 "id": "66158f543a20",
@@ -4448,7 +4493,7 @@ @@ -4448,7 +4493,7 @@
4448 "likes": 0, 4493 "likes": 0,
4449 "createdAt": 1787231440149, 4494 "createdAt": 1787231440149,
4450 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own", 4495 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own",
4451 - "lastPlayedAt": 1788233266956 4496 + "lastPlayedAt": 1788239377519
4452 }, 4497 },
4453 { 4498 {
4454 "id": "63c91f4b6bc7", 4499 "id": "63c91f4b6bc7",
@@ -4463,7 +4508,7 @@ @@ -4463,7 +4508,7 @@
4463 "likes": 0, 4508 "likes": 0,
4464 "createdAt": 1787225897650, 4509 "createdAt": 1787225897650,
4465 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own", 4510 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own",
4466 - "lastPlayedAt": 1788232632587 4511 + "lastPlayedAt": 1788236947717
4467 }, 4512 },
4468 { 4513 {
4469 "id": "ed3663dcda7f", 4514 "id": "ed3663dcda7f",
@@ -4477,7 +4522,7 @@ @@ -4477,7 +4522,7 @@
4477 "partition": "fl2va", 4522 "partition": "fl2va",
4478 "likes": 0, 4523 "likes": 0,
4479 "createdAt": 1787224701504, 4524 "createdAt": 1787224701504,
4480 - "lastPlayedAt": 1788232507824, 4525 + "lastPlayedAt": 1788238719143,
4481 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own" 4526 "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own"
4482 }, 4527 },
4483 { 4528 {
@@ -4492,7 +4537,8 @@ @@ -4492,7 +4537,8 @@
4492 "partition": "fl2va", 4537 "partition": "fl2va",
4493 "likes": 0, 4538 "likes": 0,
4494 "createdAt": 1787224519456, 4539 "createdAt": 1787224519456,
4495 - "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own" 4540 + "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own",
  4541 + "lastPlayedAt": 1788239742011
4496 }, 4542 },
4497 { 4543 {
4498 "id": "a1cc621cc6b2", 4544 "id": "a1cc621cc6b2",
@@ -4506,6 +4552,7 @@ @@ -4506,6 +4552,7 @@
4506 "partition": "ref2va", 4552 "partition": "ref2va",
4507 "likes": 0, 4553 "likes": 0,
4508 "createdAt": 1787220579189, 4554 "createdAt": 1787220579189,
4509 - "promptEn": "gothic castle at night, vampire noble carries a princess, rose petals falling" 4555 + "promptEn": "gothic castle at night, vampire noble carries a princess, rose petals falling",
  4556 + "lastPlayedAt": 1788239073351
4510 } 4557 }
4511 ] 4558 ]
4512 \ No newline at end of file 4559 \ No newline at end of file
live.css
@@ -176,32 +176,84 @@ body { @@ -176,32 +176,84 @@ body {
176 color: var(--muted); 176 color: var(--muted);
177 padding: 12px 14px 6px; 177 padding: 12px 14px 6px;
178 } 178 }
179 -.say-panel { padding: 0 14px 14px; display: flex; flex-direction: column; gap: 8px; flex: 1; }  
180 -.say-panel textarea { 179 +.chat-list {
  180 + flex: 1;
  181 + overflow-y: auto;
  182 + padding: 0 14px;
  183 + display: flex;
  184 + flex-direction: column;
  185 + gap: 8px;
  186 +}
  187 +.chat-msg { font-size: 12.5px; line-height: 1.5; word-break: break-word; }
  188 +.chat-nick { color: var(--accent); font-weight: 700; margin-right: 6px; }
  189 +.chat-text { color: #e4e4e8; }
  190 +.chat-status { display: block; font-size: 10.5px; color: var(--muted); font-weight: 700; margin-top: 1px; }
  191 +
  192 +.chat-input-row { display: flex; gap: 6px; padding: 10px 14px 4px; }
  193 +.chat-input-row input {
  194 + flex: 1;
  195 + min-width: 0;
181 background: #fff; 196 background: #fff;
182 border: none; 197 border: none;
183 - border-radius: 12px;  
184 - padding: 10px 12px; 198 + border-radius: 999px;
  199 + padding: 9px 14px;
185 color: var(--accent-text); 200 color: var(--accent-text);
186 font-weight: 700; 201 font-weight: 700;
187 font-size: 13px; 202 font-size: 13px;
188 - resize: none;  
189 - min-height: 90px;  
190 font-family: inherit; 203 font-family: inherit;
191 } 204 }
192 -.say-panel textarea::placeholder { color: #8a8a8a; font-weight: 400; }  
193 -.say-panel button { 205 +.chat-input-row input::placeholder { color: #8a8a8a; font-weight: 400; }
  206 +.chat-input-row button {
  207 + background: var(--accent);
  208 + color: var(--accent-text);
  209 + border: none;
  210 + border-radius: 999px;
  211 + padding: 0 16px;
  212 + font-weight: 700;
  213 + font-size: 13px;
  214 + cursor: pointer;
  215 +}
  216 +.chat-input-row button:disabled { opacity: .5; cursor: not-allowed; }
  217 +.say-hint { padding: 0 14px 10px; font-size: 11.5px; font-weight: 700; color: var(--muted); min-height: 1.4em; }
  218 +
  219 +/* ---- nickname modal ---- */
  220 +.nickname-modal {
  221 + position: fixed;
  222 + inset: 0;
  223 + z-index: 10;
  224 + display: flex;
  225 + align-items: center;
  226 + justify-content: center;
  227 + background: rgba(0,0,0,.7);
  228 +}
  229 +.nickname-box {
  230 + background: #fff;
  231 + color: var(--accent-text);
  232 + border-radius: 16px;
  233 + padding: 20px;
  234 + width: 260px;
  235 + display: flex;
  236 + flex-direction: column;
  237 + gap: 10px;
  238 +}
  239 +.nickname-title { font-weight: 700; font-size: 14px; }
  240 +.nickname-box input {
  241 + border: 1px solid #ddd;
  242 + border-radius: 10px;
  243 + padding: 8px 12px;
  244 + font-size: 13px;
  245 + font-family: inherit;
  246 +}
  247 +.nickname-box button {
194 background: var(--accent); 248 background: var(--accent);
195 color: var(--accent-text); 249 color: var(--accent-text);
196 border: none; 250 border: none;
197 border-radius: 999px; 251 border-radius: 999px;
198 - padding: 10px 0; 252 + padding: 9px 0;
199 font-weight: 700; 253 font-weight: 700;
200 font-size: 13px; 254 font-size: 13px;
201 cursor: pointer; 255 cursor: pointer;
202 } 256 }
203 -.say-panel button:disabled { opacity: .5; cursor: not-allowed; }  
204 -.say-hint { font-size: 11.5px; font-weight: 700; color: var(--muted); min-height: 1.4em; }  
205 257
206 /* ---- mobile sub-tabs (live mode only) ---- */ 258 /* ---- mobile sub-tabs (live mode only) ---- */
207 .mobile-subtabs { 259 .mobile-subtabs {
live.html
@@ -40,29 +40,39 @@ @@ -40,29 +40,39 @@
40 </div> 40 </div>
41 41
42 <aside class="right-col" id="rightCol" hidden> 42 <aside class="right-col" id="rightCol" hidden>
43 - <div class="section-title">接下来想看什么</div>  
44 - <div class="say-panel">  
45 - <textarea id="sayInput" placeholder="接下来想看什么…" maxlength="300"></textarea>  
46 - <button id="sayBtn">发送</button>  
47 - <div class="say-hint" id="sayHint"></div> 43 + <div class="section-title">聊天</div>
  44 + <div class="chat-list" id="chatList"></div>
  45 + <div class="chat-input-row">
  46 + <input type="text" id="chatInput" placeholder="说点什么,或者想看什么…" maxlength="300" />
  47 + <button id="chatSendBtn">发送</button>
48 </div> 48 </div>
  49 + <div class="say-hint" id="chatHint"></div>
49 </aside> 50 </aside>
50 51
51 <!-- flex item of #body (not .channel) so `order` in the mobile media 52 <!-- flex item of #body (not .channel) so `order` in the mobile media
52 query actually places it between the player and left/right-col. --> 53 query actually places it between the player and left/right-col. -->
53 <div class="mobile-subtabs" id="mobileSubtabs" hidden> 54 <div class="mobile-subtabs" id="mobileSubtabs" hidden>
54 <button data-sub="queue" class="active">排队队列</button> 55 <button data-sub="queue" class="active">排队队列</button>
55 - <button data-sub="input">输入</button> 56 + <button data-sub="input">聊天</button>
56 </div> 57 </div>
57 </div> 58 </div>
58 </div> 59 </div>
59 60
  61 +<div class="nickname-modal" id="nicknameModal" hidden>
  62 + <div class="nickname-box">
  63 + <div class="nickname-title">先起个昵称才能发言</div>
  64 + <input type="text" id="nicknameInput" placeholder="你的昵称" maxlength="20" />
  65 + <button id="nicknameConfirmBtn">确定</button>
  66 + </div>
  67 +</div>
  68 +
60 <script> 69 <script>
61 let mode = "live"; 70 let mode = "live";
62 let clips = []; // browse mode: full list 71 let clips = []; // browse mode: full list
63 let currentIndex = -1; 72 let currentIndex = -1;
64 let currentClipId = null; 73 let currentClipId = null;
65 let livePollTimer = null; 74 let livePollTimer = null;
  75 +let chatPollTimer = null;
66 let advanceTimer = null; // precise, server-endAt-driven switch to the next clip — the 4s poll is just a resync/fallback 76 let advanceTimer = null; // precise, server-endAt-driven switch to the next clip — the 4s poll is just a resync/fallback
67 let upNextCache = []; 77 let upNextCache = [];
68 let mobileSub = "queue"; 78 let mobileSub = "queue";
@@ -75,11 +85,15 @@ const liveDot = document.getElementById(&quot;liveDot&quot;); @@ -75,11 +85,15 @@ const liveDot = document.getElementById(&quot;liveDot&quot;);
75 const leftCol = document.getElementById("leftCol"); 85 const leftCol = document.getElementById("leftCol");
76 const rightCol = document.getElementById("rightCol"); 86 const rightCol = document.getElementById("rightCol");
77 const mobileSubtabs = document.getElementById("mobileSubtabs"); 87 const mobileSubtabs = document.getElementById("mobileSubtabs");
78 -const sayInput = document.getElementById("sayInput");  
79 -const sayBtn = document.getElementById("sayBtn");  
80 -const sayHint = document.getElementById("sayHint"); 88 +const chatList = document.getElementById("chatList");
  89 +const chatInput = document.getElementById("chatInput");
  90 +const chatSendBtn = document.getElementById("chatSendBtn");
  91 +const chatHint = document.getElementById("chatHint");
81 const watchingCount = document.getElementById("watchingCount"); 92 const watchingCount = document.getElementById("watchingCount");
82 const muteBtn = document.getElementById("muteBtn"); 93 const muteBtn = document.getElementById("muteBtn");
  94 +const nicknameModal = document.getElementById("nicknameModal");
  95 +const nicknameInput = document.getElementById("nicknameInput");
  96 +const nicknameConfirmBtn = document.getElementById("nicknameConfirmBtn");
83 const clientId = Math.random().toString(36).slice(2); 97 const clientId = Math.random().toString(36).slice(2);
84 98
85 muteBtn.addEventListener("click", () => { 99 muteBtn.addEventListener("click", () => {
@@ -96,6 +110,40 @@ function escapeHtml(s) { @@ -96,6 +110,40 @@ function escapeHtml(s) {
96 return div.innerHTML; 110 return div.innerHTML;
97 } 111 }
98 112
  113 +// ---- viewer identity: nickname + uid, stored in cookies ----
  114 +function getCookie(name) {
  115 + const m = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
  116 + return m ? decodeURIComponent(m[1]) : null;
  117 +}
  118 +function setCookie(name, value, days) {
  119 + document.cookie = `${name}=${encodeURIComponent(value)}; max-age=${days * 86400}; path=/; samesite=lax`;
  120 +}
  121 +function getIdentity() {
  122 + const uid = getCookie("viewer_uid");
  123 + const nickname = getCookie("viewer_nickname");
  124 + return uid && nickname ? { uid, nickname } : null;
  125 +}
  126 +function ensureIdentity() {
  127 + const existing = getIdentity();
  128 + if (existing) return Promise.resolve(existing);
  129 + return new Promise((resolve) => {
  130 + nicknameModal.hidden = false;
  131 + nicknameInput.value = "";
  132 + nicknameInput.focus();
  133 + const onConfirm = () => {
  134 + const nickname = nicknameInput.value.trim().slice(0, 20);
  135 + if (!nickname) return;
  136 + const uid = (crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`);
  137 + setCookie("viewer_uid", uid, 365);
  138 + setCookie("viewer_nickname", nickname, 365);
  139 + nicknameModal.hidden = true;
  140 + nicknameConfirmBtn.removeEventListener("click", onConfirm);
  141 + resolve({ uid, nickname });
  142 + };
  143 + nicknameConfirmBtn.addEventListener("click", onConfirm);
  144 + });
  145 +}
  146 +
99 function renderBrowseList() { 147 function renderBrowseList() {
100 if (clips.length === 0) { 148 if (clips.length === 0) {
101 clipList.innerHTML = `<div class="empty-hint">片段库为空</div>`; 149 clipList.innerHTML = `<div class="empty-hint">片段库为空</div>`;
@@ -148,16 +196,29 @@ async function loadBrowse(sort) { @@ -148,16 +196,29 @@ async function loadBrowse(sort) {
148 if (clips.length) playBrowse(0); 196 if (clips.length) playBrowse(0);
149 } 197 }
150 198
  199 +// Left queue: prompt + who asked for it only — no resolution/duration/date.
151 function renderLiveList(state) { 200 function renderLiveList(state) {
152 listTitle.textContent = "排队队列"; 201 listTitle.textContent = "排队队列";
153 const upcoming = state.upNext || []; 202 const upcoming = state.upNext || [];
154 const queue = state.queue || []; 203 const queue = state.queue || [];
155 const statusLabel = { generating: "生成中…", matched: "即将播放", failed: "❌ 生成失败" }; 204 const statusLabel = { generating: "生成中…", matched: "即将播放", failed: "❌ 生成失败" };
156 const queueHtml = queue 205 const queueHtml = queue
157 - .map((q) => `<div class="clip-row"><div class="prompt">💬 ${escapeHtml(q.prompt)}</div><div class="meta">${statusLabel[q.status] || q.status}</div></div>`) 206 + .map(
  207 + (q) => `
  208 + <div class="clip-row">
  209 + <div class="prompt">💬 ${escapeHtml(q.prompt)}</div>
  210 + <div class="meta">${q.nickname ? `${escapeHtml(q.nickname)} · ` : ""}${statusLabel[q.status] || q.status}</div>
  211 + </div>`
  212 + )
158 .join(""); 213 .join("");
159 const upcomingHtml = upcoming 214 const upcomingHtml = upcoming
160 - .map((c) => `<div class="clip-row"><div class="prompt">${escapeHtml(c.prompt)}</div><div class="meta">${fmtMeta(c)}</div></div>`) 215 + .map(
  216 + (c) => `
  217 + <div class="clip-row">
  218 + <div class="prompt">${escapeHtml(c.prompt)}</div>
  219 + ${c.requestedBy ? `<div class="meta">by ${escapeHtml(c.requestedBy)}</div>` : ""}
  220 + </div>`
  221 + )
161 .join(""); 222 .join("");
162 clipList.innerHTML = queueHtml + upcomingHtml || `<div class="empty-hint">暂无排队</div>`; 223 clipList.innerHTML = queueHtml + upcomingHtml || `<div class="empty-hint">暂无排队</div>`;
163 } 224 }
@@ -207,33 +268,54 @@ async function pollLive() { @@ -207,33 +268,54 @@ async function pollLive() {
207 } 268 }
208 } 269 }
209 270
210 -async function submitPrompt() {  
211 - const prompt = sayInput.value.trim();  
212 - if (!prompt) return;  
213 - sayBtn.disabled = true; 271 +const chatStatusLabel = { rejected: "❌ 未通过审核", generating: "生成中…", matched: "即将播放", scheduled: "即将播放", ready: "已排播放", failed: "❌ 生成失败" };
  272 +function renderChat(messages) {
  273 + const atBottom = chatList.scrollHeight - chatList.scrollTop - chatList.clientHeight < 40;
  274 + chatList.innerHTML = messages
  275 + .map((m) => {
  276 + const status = m.status && chatStatusLabel[m.status] ? `<span class="chat-status">${chatStatusLabel[m.status]}</span>` : "";
  277 + return `<div class="chat-msg"><span class="chat-nick">${escapeHtml(m.nickname)}</span><span class="chat-text">${escapeHtml(m.text)}</span>${status}</div>`;
  278 + })
  279 + .join("");
  280 + if (atBottom) chatList.scrollTop = chatList.scrollHeight;
  281 +}
  282 +
  283 +async function pollChat() {
214 try { 284 try {
215 - const res = await fetch("/api/prompt", { 285 + const res = await fetch(`/api/chat?cid=${clientId}`);
  286 + renderChat(await res.json());
  287 + } catch {
  288 + // transient — next poll retries
  289 + }
  290 +}
  291 +
  292 +async function sendChat() {
  293 + const text = chatInput.value.trim();
  294 + if (!text) return;
  295 + const identity = await ensureIdentity();
  296 + chatSendBtn.disabled = true;
  297 + try {
  298 + const res = await fetch("/api/chat", {
216 method: "POST", 299 method: "POST",
217 headers: { "Content-Type": "application/json" }, 300 headers: { "Content-Type": "application/json" },
218 - body: JSON.stringify({ prompt }), 301 + body: JSON.stringify({ uid: identity.uid, nickname: identity.nickname, text }),
219 }); 302 });
220 const data = await res.json(); 303 const data = await res.json();
221 if (res.status === 429) { 304 if (res.status === 429) {
222 - sayHint.textContent = data.error; 305 + chatHint.textContent = data.error;
223 return; 306 return;
224 } 307 }
225 - sayInput.value = "";  
226 - sayHint.textContent = data.status === "matched" ? "已匹配片段,即将播放" : data.status === "generating" ? "库里没有,已提交生成,请稍候…" : ""; 308 + chatInput.value = "";
  309 + chatHint.textContent =
  310 + data.status === "rejected" ? `未通过审核:${data.reason}` : data.status === "matched" ? "已匹配片段,即将播放" : data.status === "generating" ? "库里没有,已提交生成,请稍候…" : "";
  311 + pollChat();
227 } finally { 312 } finally {
228 - sayBtn.disabled = false; 313 + chatSendBtn.disabled = false;
229 } 314 }
230 } 315 }
231 -sayBtn.addEventListener("click", submitPrompt);  
232 -sayInput.addEventListener("keydown", (e) => {  
233 - if (e.key === "Enter" && !e.shiftKey) {  
234 - e.preventDefault();  
235 - submitPrompt();  
236 - } 316 +chatSendBtn.addEventListener("click", sendChat);
  317 +chatInput.addEventListener("keydown", (e) => {
  318 + if (e.key === "Enter") sendChat();
237 }); 319 });
238 320
239 function applyMobileSub() { 321 function applyMobileSub() {
@@ -252,6 +334,7 @@ async function setMode(newMode) { @@ -252,6 +334,7 @@ async function setMode(newMode) {
252 mode = newMode; 334 mode = newMode;
253 document.querySelectorAll("#tabs button").forEach((b) => b.classList.toggle("active", b.dataset.mode === newMode)); 335 document.querySelectorAll("#tabs button").forEach((b) => b.classList.toggle("active", b.dataset.mode === newMode));
254 clearInterval(livePollTimer); 336 clearInterval(livePollTimer);
  337 + clearInterval(chatPollTimer);
255 clearTimeout(advanceTimer); 338 clearTimeout(advanceTimer);
256 currentClipId = null; 339 currentClipId = null;
257 if (newMode === "live") { 340 if (newMode === "live") {
@@ -259,8 +342,9 @@ async function setMode(newMode) { @@ -259,8 +342,9 @@ async function setMode(newMode) {
259 rightCol.hidden = false; 342 rightCol.hidden = false;
260 mobileSubtabs.hidden = false; 343 mobileSubtabs.hidden = false;
261 applyMobileSub(); 344 applyMobileSub();
262 - await pollLive(); 345 + await Promise.all([pollLive(), pollChat()]);
263 livePollTimer = setInterval(pollLive, 4000); 346 livePollTimer = setInterval(pollLive, 4000);
  347 + chatPollTimer = setInterval(pollChat, 3000);
264 } else { 348 } else {
265 liveDot.hidden = true; 349 liveDot.hidden = true;
266 rightCol.hidden = true; 350 rightCol.hidden = true;
server.js
@@ -39,13 +39,13 @@ const LIVE_STATE_PATH = path.join(__dirname, &quot;live-state.json&quot;); @@ -39,13 +39,13 @@ const LIVE_STATE_PATH = path.join(__dirname, &quot;live-state.json&quot;);
39 function loadLiveState() { 39 function loadLiveState() {
40 try { 40 try {
41 const s = JSON.parse(fs.readFileSync(LIVE_STATE_PATH, "utf8")); 41 const s = JSON.parse(fs.readFileSync(LIVE_STATE_PATH, "utf8"));
42 - return { schedule: s.schedule || [], promptQueue: s.promptQueue || [] }; 42 + return { schedule: s.schedule || [], promptQueue: s.promptQueue || [], chatMessages: s.chatMessages || [] };
43 } catch { 43 } catch {
44 - return { schedule: [], promptQueue: [] }; 44 + return { schedule: [], promptQueue: [], chatMessages: [] };
45 } 45 }
46 } 46 }
47 function saveLiveState() { 47 function saveLiveState() {
48 - fs.writeFileSync(LIVE_STATE_PATH, JSON.stringify({ schedule, promptQueue }, null, 2)); 48 + fs.writeFileSync(LIVE_STATE_PATH, JSON.stringify({ schedule, promptQueue, chatMessages }, null, 2));
49 } 49 }
50 50
51 function readJsonBody(req) { 51 function readJsonBody(req) {
@@ -101,8 +101,52 @@ const H3_GEN_PARAMS = { width: 832, height: 480, frames: 120, steps: 20, turbo: @@ -101,8 +101,52 @@ const H3_GEN_PARAMS = { width: 832, height: 480, frames: 120, steps: 20, turbo:
101 const FPS = 24; 101 const FPS = 24;
102 102
103 const initialLiveState = loadLiveState(); 103 const initialLiveState = loadLiveState();
104 -let schedule = initialLiveState.schedule; // [{clip, startAt, endAt}]  
105 -let promptQueue = initialLiveState.promptQueue; // [{id, prompt, status, clip?, error?, submittedAt, h3JobId?}] 104 +let schedule = initialLiveState.schedule; // [{clip, startAt, endAt, requestedBy?}]
  105 +let promptQueue = initialLiveState.promptQueue; // [{id, prompt, nickname?, status, clip?, error?, submittedAt, h3JobId?}]
  106 +let chatMessages = initialLiveState.chatMessages; // [{id, uid, nickname, text, createdAt, status}]
  107 +
  108 +// ---- chat + moderation ----
  109 +// ponytail: a starter blocklist, not a real content-policy list — swap in
  110 +// whatever the actual moderation requirements are. Substring match on the
  111 +// lowercased text; cheap and deterministic. Anything this doesn't catch
  112 +// (content that's fine as *text* but unsafe to *generate*) falls through to
  113 +// the H3 console's own generation-time safety checker — a rejected/failed
  114 +// job there just surfaces as a normal "failed" queue entry.
  115 +const BANNED_WORDS = ["色情", "porn", "自杀", "血腥暴力", "毒品"];
  116 +function moderateText(text) {
  117 + const lower = text.toLowerCase();
  118 + const hit = BANNED_WORDS.find((w) => lower.includes(w));
  119 + if (hit) return { ok: false, reason: `包含违禁词` };
  120 + return { ok: true };
  121 +}
  122 +
  123 +const CHAT_HISTORY_LIMIT = 300;
  124 +function pushChatMessage(msg) {
  125 + chatMessages.push(msg);
  126 + if (chatMessages.length > CHAT_HISTORY_LIMIT) chatMessages = chatMessages.slice(-CHAT_HISTORY_LIMIT);
  127 +}
  128 +function updateChatStatus(id, status) {
  129 + const msg = chatMessages.find((m) => m.id === id);
  130 + if (msg) msg.status = status;
  131 +}
  132 +
  133 +// Shared by /api/chat (after moderation) and /api/prompt (direct API use,
  134 +// e.g. testing) — looks up a library match or kicks off generation, exactly
  135 +// like before chat existed. `nickname` is optional (empty for /api/prompt).
  136 +function submitPromptToQueue(id, prompt, nickname) {
  137 + const entry = { id, prompt, nickname: nickname || null, submittedAt: Date.now() };
  138 + const match = findLibraryMatch(normalizePrompt(prompt));
  139 + if (match) {
  140 + entry.status = "matched";
  141 + entry.clip = match;
  142 + promptQueue.push(entry);
  143 + return { status: "matched", clip: match };
  144 + }
  145 + entry.status = "generating";
  146 + promptQueue.push(entry);
  147 + generateForPrompt(entry); // fire and forget — polled via /api/live/state
  148 + return { status: "generating", id: entry.id };
  149 +}
106 150
107 function normalizePrompt(s) { 151 function normalizePrompt(s) {
108 return s.trim().toLowerCase().replace(/\s+/g, " "); 152 return s.trim().toLowerCase().replace(/\s+/g, " ");
@@ -138,11 +182,19 @@ function markPlayed(id) { @@ -138,11 +182,19 @@ function markPlayed(id) {
138 // status:"scheduled" — once a clip is in `schedule` it shows up via 182 // status:"scheduled" — once a clip is in `schedule` it shows up via
139 // current/upNext, so keeping a second, never-cleared copy in promptQueue 183 // current/upNext, so keeping a second, never-cleared copy in promptQueue
140 // just grows forever and duplicates the same info. 184 // just grows forever and duplicates the same info.
  185 +// Returns {clip, requestedBy, chatId} — chatId lets the caller flip the
  186 +// originating chat message to "scheduled" once it's actually about to air.
141 function pickNextClip() { 187 function pickNextClip() {
142 const readyIdx = promptQueue.findIndex((q) => q.status === "ready"); 188 const readyIdx = promptQueue.findIndex((q) => q.status === "ready");
143 - if (readyIdx !== -1) return promptQueue.splice(readyIdx, 1)[0].clip; 189 + if (readyIdx !== -1) {
  190 + const q = promptQueue.splice(readyIdx, 1)[0];
  191 + return { clip: q.clip, requestedBy: q.nickname, chatId: q.id };
  192 + }
144 const matchedIdx = promptQueue.findIndex((q) => q.status === "matched"); 193 const matchedIdx = promptQueue.findIndex((q) => q.status === "matched");
145 - if (matchedIdx !== -1) return promptQueue.splice(matchedIdx, 1)[0].clip; 194 + if (matchedIdx !== -1) {
  195 + const q = promptQueue.splice(matchedIdx, 1)[0];
  196 + return { clip: q.clip, requestedBy: q.nickname, chatId: q.id };
  197 + }
146 198
147 const lib = getLibrary(); 199 const lib = getLibrary();
148 if (!lib.length) return null; 200 if (!lib.length) return null;
@@ -153,7 +205,7 @@ function pickNextClip() { @@ -153,7 +205,7 @@ function pickNextClip() {
153 // random among everything that isn't on cooldown, no bias toward "fresh". 205 // random among everything that isn't on cooldown, no bias toward "fresh".
154 const offCooldown = notPrev.filter((c) => !c.lastPlayedAt || now - c.lastPlayedAt > REPEAT_COOLDOWN_MS); 206 const offCooldown = notPrev.filter((c) => !c.lastPlayedAt || now - c.lastPlayedAt > REPEAT_COOLDOWN_MS);
155 const pool = offCooldown.length ? offCooldown : notPrev; 207 const pool = offCooldown.length ? offCooldown : notPrev;
156 - return pool[Math.floor(Math.random() * pool.length)]; 208 + return { clip: pool[Math.floor(Math.random() * pool.length)], requestedBy: null, chatId: null };
157 } 209 }
158 210
159 const FAILED_ENTRY_TTL_MS = 20000; // how long a "generation failed" entry stays visible before cleanup 211 const FAILED_ENTRY_TTL_MS = 20000; // how long a "generation failed" entry stays visible before cleanup
@@ -162,11 +214,13 @@ function ensureSchedule() { @@ -162,11 +214,13 @@ function ensureSchedule() {
162 const now = Date.now(); 214 const now = Date.now();
163 while (schedule.length > 1 && schedule[1].endAt <= now) schedule.shift(); 215 while (schedule.length > 1 && schedule[1].endAt <= now) schedule.shift();
164 while (schedule.length === 0 || schedule[schedule.length - 1].endAt < now + FILLER_DURATION_MS) { 216 while (schedule.length === 0 || schedule[schedule.length - 1].endAt < now + FILLER_DURATION_MS) {
165 - const clip = pickNextClip();  
166 - if (!clip) break; 217 + const picked = pickNextClip();
  218 + if (!picked || !picked.clip) break;
  219 + const { clip, requestedBy, chatId } = picked;
167 markPlayed(clip.id); 220 markPlayed(clip.id);
  221 + if (chatId) updateChatStatus(chatId, "scheduled");
168 const startAt = schedule.length ? schedule[schedule.length - 1].endAt : now; 222 const startAt = schedule.length ? schedule[schedule.length - 1].endAt : now;
169 - schedule.push({ clip, startAt, endAt: startAt + clip.duration * 1000 }); 223 + schedule.push({ clip, startAt, endAt: startAt + clip.duration * 1000, requestedBy });
170 } 224 }
171 promptQueue = promptQueue.filter((q) => q.status !== "failed" || now - q.failedAt < FAILED_ENTRY_TTL_MS); 225 promptQueue = promptQueue.filter((q) => q.status !== "failed" || now - q.failedAt < FAILED_ENTRY_TTL_MS);
172 226
@@ -229,6 +283,7 @@ async function generateForPrompt(entry) { @@ -229,6 +283,7 @@ async function generateForPrompt(entry) {
229 saveLibrary(lib); 283 saveLibrary(lib);
230 entry.status = "ready"; 284 entry.status = "ready";
231 entry.clip = clip; 285 entry.clip = clip;
  286 + updateChatStatus(entry.id, "ready");
232 saveLiveState(); 287 saveLiveState();
233 return; 288 return;
234 } 289 }
@@ -239,6 +294,7 @@ async function generateForPrompt(entry) { @@ -239,6 +294,7 @@ async function generateForPrompt(entry) {
239 entry.status = "failed"; 294 entry.status = "failed";
240 entry.error = err.message; 295 entry.error = err.message;
241 entry.failedAt = Date.now(); 296 entry.failedAt = Date.now();
  297 + updateChatStatus(entry.id, "failed");
242 saveLiveState(); 298 saveLiveState();
243 } 299 }
244 } 300 }
@@ -310,11 +366,11 @@ http @@ -310,11 +366,11 @@ http
310 JSON.stringify({ 366 JSON.stringify({
311 now, 367 now,
312 watching: viewers.size, 368 watching: viewers.size,
313 - current: cur ? { clip: cur.clip, startAt: cur.startAt, endAt: cur.endAt } : null,  
314 - upNext: schedule.slice(idx + 1, idx + 4).map((s) => s.clip), 369 + current: cur ? { clip: cur.clip, startAt: cur.startAt, endAt: cur.endAt, requestedBy: cur.requestedBy || null } : null,
  370 + upNext: schedule.slice(idx + 1, idx + 4).map((s) => ({ ...s.clip, requestedBy: s.requestedBy || null })),
315 queue: promptQueue 371 queue: promptQueue
316 .filter((q) => ["matched", "generating", "failed"].includes(q.status)) 372 .filter((q) => ["matched", "generating", "failed"].includes(q.status))
317 - .map((q) => ({ prompt: q.prompt, status: q.status, error: q.error })), 373 + .map((q) => ({ prompt: q.prompt, nickname: q.nickname, status: q.status, error: q.error })),
318 }) 374 })
319 ); 375 );
320 return; 376 return;
@@ -334,23 +390,63 @@ http @@ -334,23 +390,63 @@ http
334 res.end(JSON.stringify({ error: "prompt required, max 300 chars" })); 390 res.end(JSON.stringify({ error: "prompt required, max 300 chars" }));
335 return; 391 return;
336 } 392 }
337 - const entry = { id: Date.now().toString(36), prompt, submittedAt: Date.now() };  
338 - const match = findLibraryMatch(normalizePrompt(prompt));  
339 - if (match) {  
340 - entry.status = "matched";  
341 - entry.clip = match;  
342 - promptQueue.push(entry);  
343 - saveLiveState();  
344 - res.writeHead(200, { "Content-Type": "application/json" });  
345 - res.end(JSON.stringify({ status: "matched", clip: match }));  
346 - } else {  
347 - entry.status = "generating";  
348 - promptQueue.push(entry); 393 + const result = submitPromptToQueue(Date.now().toString(36), prompt, null);
  394 + saveLiveState();
  395 + res.writeHead(200, { "Content-Type": "application/json" });
  396 + res.end(JSON.stringify(result));
  397 + })
  398 + .catch(() => {
  399 + res.writeHead(400, { "Content-Type": "application/json" });
  400 + res.end(JSON.stringify({ error: "invalid JSON body" }));
  401 + });
  402 + return;
  403 + }
  404 +
  405 + if (pathname === "/api/chat" && req.method === "GET") {
  406 + const cid = new URL(req.url, "http://x").searchParams.get("cid");
  407 + if (cid) viewers.set(cid, Date.now());
  408 + res.writeHead(200, { "Content-Type": "application/json" });
  409 + res.end(JSON.stringify(chatMessages.slice(-100)));
  410 + return;
  411 + }
  412 +
  413 + if (pathname === "/api/chat" && req.method === "POST") {
  414 + if (isRateLimited(req.socket.remoteAddress)) {
  415 + res.writeHead(429, { "Content-Type": "application/json" });
  416 + res.end(JSON.stringify({ error: `最多每 ${RATE_LIMIT_WINDOW_MS / 60000} 分钟发送 ${RATE_LIMIT_MAX} 条,请稍后再试` }));
  417 + return;
  418 + }
  419 + readJsonBody(req)
  420 + .then((body) => {
  421 + const uid = (body.uid || "").trim();
  422 + const nickname = (body.nickname || "").trim().slice(0, 20);
  423 + const text = (body.text || "").trim();
  424 + if (!uid || !nickname || !text || text.length > 300) {
  425 + res.writeHead(400, { "Content-Type": "application/json" });
  426 + res.end(JSON.stringify({ error: "uid, nickname, text required, text max 300 chars" }));
  427 + return;
  428 + }
  429 + const id = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
  430 + const msg = { id, uid, nickname, text, createdAt: Date.now() };
  431 +
  432 + const mod = moderateText(text);
  433 + if (!mod.ok) {
  434 + msg.status = "rejected";
  435 + msg.rejectReason = mod.reason;
  436 + pushChatMessage(msg);
349 saveLiveState(); 437 saveLiveState();
350 - generateForPrompt(entry); // fire and forget — polled via /api/live/state  
351 res.writeHead(200, { "Content-Type": "application/json" }); 438 res.writeHead(200, { "Content-Type": "application/json" });
352 - res.end(JSON.stringify({ status: "generating", id: entry.id })); 439 + res.end(JSON.stringify({ status: "rejected", reason: mod.reason }));
  440 + return;
353 } 441 }
  442 +
  443 + msg.status = "approved";
  444 + pushChatMessage(msg);
  445 + const result = submitPromptToQueue(id, text, nickname);
  446 + msg.status = result.status; // "matched" | "generating"
  447 + saveLiveState();
  448 + res.writeHead(200, { "Content-Type": "application/json" });
  449 + res.end(JSON.stringify(result));
354 }) 450 })
355 .catch(() => { 451 .catch(() => {
356 res.writeHead(400, { "Content-Type": "application/json" }); 452 res.writeHead(400, { "Content-Type": "application/json" });