Commit edfe6543d392231836d45c98040351895ae4868b
1 parent
fcb9a940
Rotate the random fallback pick by play count, not pure chance
Adds playCount to each library clip, incremented every time it airs (markPlayed). The idle-filler pick in pickNextClip now randomizes only among the least-played clips in the eligible pool — everyone at 0 plays first, then everyone tied at 1, and so on — instead of picking uniformly at random across the whole library, which let some clips rack up far more airtime than others by chance alone. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0144MiJ2cLjMd9jWaeEpVfUJ
Showing
2 changed files
with
59 additions
and
53 deletions
library.json
| ... | ... | @@ -10,7 +10,8 @@ |
| 10 | 10 | "duration": 1, |
| 11 | 11 | "partition": "fl2va", |
| 12 | 12 | "likes": 0, |
| 13 | - "createdAt": 1788251288958 | |
| 13 | + "createdAt": 1788251288958, | |
| 14 | + "lastPlayedAt": 1788254550116 | |
| 14 | 15 | }, |
| 15 | 16 | { |
| 16 | 17 | "id": "8024972d6019", |
| ... | ... | @@ -379,7 +380,7 @@ |
| 379 | 380 | "likes": 0, |
| 380 | 381 | "createdAt": 1788181569491, |
| 381 | 382 | "promptEn": "a massive warship glides through the stars, the bridge suddenly shakes hard", |
| 382 | - "lastPlayedAt": 1788253093423 | |
| 383 | + "lastPlayedAt": 1788254390052 | |
| 383 | 384 | }, |
| 384 | 385 | { |
| 385 | 386 | "id": "44bf5b969e94", |
| ... | ... | @@ -424,7 +425,7 @@ |
| 424 | 425 | "likes": 0, |
| 425 | 426 | "createdAt": 1788159367556, |
| 426 | 427 | "promptEn": "constable talking nervously in an old room, looking scared", |
| 427 | - "lastPlayedAt": 1788252509170 | |
| 428 | + "lastPlayedAt": 1788254306023 | |
| 428 | 429 | }, |
| 429 | 430 | { |
| 430 | 431 | "id": "a6d767413426", |
| ... | ... | @@ -439,7 +440,7 @@ |
| 439 | 440 | "likes": 0, |
| 440 | 441 | "createdAt": 1788157322611, |
| 441 | 442 | "promptEn": "constable talking, looking scared", |
| 442 | - "lastPlayedAt": 1788250916299 | |
| 443 | + "lastPlayedAt": 1788254071916 | |
| 443 | 444 | }, |
| 444 | 445 | { |
| 445 | 446 | "id": "7977eeeabe8b", |
| ... | ... | @@ -469,7 +470,7 @@ |
| 469 | 470 | "likes": 0, |
| 470 | 471 | "createdAt": 1788156878022, |
| 471 | 472 | "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen", |
| 472 | - "lastPlayedAt": 1788252225046 | |
| 473 | + "lastPlayedAt": 1788254209972 | |
| 473 | 474 | }, |
| 474 | 475 | { |
| 475 | 476 | "id": "a0b752e7ac0d", |
| ... | ... | @@ -544,7 +545,7 @@ |
| 544 | 545 | "likes": 0, |
| 545 | 546 | "createdAt": 1788120171608, |
| 546 | 547 | "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen", |
| 547 | - "lastPlayedAt": 1788252411133 | |
| 548 | + "lastPlayedAt": 1788254175959 | |
| 548 | 549 | }, |
| 549 | 550 | { |
| 550 | 551 | "id": "b8c762a3672e", |
| ... | ... | @@ -574,7 +575,7 @@ |
| 574 | 575 | "likes": 0, |
| 575 | 576 | "createdAt": 1788085282201, |
| 576 | 577 | "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen", |
| 577 | - "lastPlayedAt": 1788251288482 | |
| 578 | + "lastPlayedAt": 1788254023891 | |
| 578 | 579 | }, |
| 579 | 580 | { |
| 580 | 581 | "id": "683ab2fd4681", |
| ... | ... | @@ -589,7 +590,7 @@ |
| 589 | 590 | "likes": 0, |
| 590 | 591 | "createdAt": 1788071067702, |
| 591 | 592 | "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen", |
| 592 | - "lastPlayedAt": 1788251524674 | |
| 593 | + "lastPlayedAt": 1788253999874 | |
| 593 | 594 | }, |
| 594 | 595 | { |
| 595 | 596 | "id": "2e09fcd3d42c", |
| ... | ... | @@ -649,7 +650,7 @@ |
| 649 | 650 | "likes": 0, |
| 650 | 651 | "createdAt": 1788027833836, |
| 651 | 652 | "promptEn": "wounded hunter at a ruined subway dungeon entrance, lightning flickers, pointing into the tunnel", |
| 652 | - "lastPlayedAt": 1788250480118 | |
| 653 | + "lastPlayedAt": 1788254268003 | |
| 653 | 654 | }, |
| 654 | 655 | { |
| 655 | 656 | "id": "cde399c27dc4", |
| ... | ... | @@ -754,7 +755,7 @@ |
| 754 | 755 | "likes": 0, |
| 755 | 756 | "createdAt": 1788026568242, |
| 756 | 757 | "promptEn": "magma commander in a white coat, raising a lava fist on the battlefield", |
| 757 | - "lastPlayedAt": 1788252487163 | |
| 758 | + "lastPlayedAt": 1788254205974 | |
| 758 | 759 | }, |
| 759 | 760 | { |
| 760 | 761 | "id": "1442013e94f0", |
| ... | ... | @@ -784,7 +785,7 @@ |
| 784 | 785 | "likes": 0, |
| 785 | 786 | "createdAt": 1788025642732, |
| 786 | 787 | "promptEn": "Vecna reaching out a claw in his lair in the Upside Down, red lightning", |
| 787 | - "lastPlayedAt": 1788252559192 | |
| 788 | + "lastPlayedAt": 1788254043902 | |
| 788 | 789 | }, |
| 789 | 790 | { |
| 790 | 791 | "id": "00862f966c3f", |
| ... | ... | @@ -904,7 +905,7 @@ |
| 904 | 905 | "likes": 0, |
| 905 | 906 | "createdAt": 1788025220954, |
| 906 | 907 | "promptEn": "pirate captain plants a cutlass on the table, making a dangerous bargain", |
| 907 | - "lastPlayedAt": 1788251456618 | |
| 908 | + "lastPlayedAt": 1788254095922 | |
| 908 | 909 | }, |
| 909 | 910 | { |
| 910 | 911 | "id": "3e03975ff40a", |
| ... | ... | @@ -919,7 +920,7 @@ |
| 919 | 920 | "likes": 0, |
| 920 | 921 | "createdAt": 1788025003469, |
| 921 | 922 | "promptEn": "vampire idol with red eyes, offering a blood-red contract backstage", |
| 922 | - "lastPlayedAt": 1788243983188 | |
| 923 | + "lastPlayedAt": 1788254386050 | |
| 923 | 924 | }, |
| 924 | 925 | { |
| 925 | 926 | "id": "2857aacf11bb", |
| ... | ... | @@ -1054,7 +1055,7 @@ |
| 1054 | 1055 | "likes": 0, |
| 1055 | 1056 | "createdAt": 1788015737873, |
| 1056 | 1057 | "promptEn": "pirate negotiation, went in head-on and Barbossa saw it coming", |
| 1057 | - "lastPlayedAt": 1788252825264 | |
| 1058 | + "lastPlayedAt": 1788254215976 | |
| 1058 | 1059 | }, |
| 1059 | 1060 | { |
| 1060 | 1061 | "id": "2a5ee87b8f13", |
| ... | ... | @@ -1174,7 +1175,7 @@ |
| 1174 | 1175 | "likes": 0, |
| 1175 | 1176 | "createdAt": 1788013870849, |
| 1176 | 1177 | "promptEn": "glass bridge game, exploiting a flaw to push back player 101", |
| 1177 | - "lastPlayedAt": 1788249963847 | |
| 1178 | + "lastPlayedAt": 1788254201971 | |
| 1178 | 1179 | }, |
| 1179 | 1180 | { |
| 1180 | 1181 | "id": "dd12bbb55060", |
| ... | ... | @@ -1189,7 +1190,7 @@ |
| 1189 | 1190 | "likes": 0, |
| 1190 | 1191 | "createdAt": 1788013634333, |
| 1191 | 1192 | "promptEn": "glass bridge game, held the goal but paid a price", |
| 1192 | - "lastPlayedAt": 1788252773238 | |
| 1193 | + "lastPlayedAt": 1788254355883 | |
| 1193 | 1194 | }, |
| 1194 | 1195 | { |
| 1195 | 1196 | "id": "5b8ef21616d4", |
| ... | ... | @@ -1294,7 +1295,7 @@ |
| 1294 | 1295 | "likes": 0, |
| 1295 | 1296 | "createdAt": 1788012441458, |
| 1296 | 1297 | "promptEn": "facing Vecna, held the goal but paid a price", |
| 1297 | - "lastPlayedAt": 1788251798845 | |
| 1298 | + "lastPlayedAt": 1788254283882 | |
| 1298 | 1299 | }, |
| 1299 | 1300 | { |
| 1300 | 1301 | "id": "691c2f679759", |
| ... | ... | @@ -1429,7 +1430,7 @@ |
| 1429 | 1430 | "likes": 0, |
| 1430 | 1431 | "createdAt": 1788010775613, |
| 1431 | 1432 | "promptEn": "facing Voldemort, stayed passive and got cornered", |
| 1432 | - "lastPlayedAt": 1788252633224 | |
| 1433 | + "lastPlayedAt": 1788254061908 | |
| 1433 | 1434 | }, |
| 1434 | 1435 | { |
| 1435 | 1436 | "id": "795842efa4a1", |
| ... | ... | @@ -1474,7 +1475,7 @@ |
| 1474 | 1475 | "likes": 0, |
| 1475 | 1476 | "createdAt": 1788010062535, |
| 1476 | 1477 | "promptEn": "facing Thanos, held the goal but paid a price", |
| 1477 | - "lastPlayedAt": 1788252076972 | |
| 1478 | + "lastPlayedAt": 1788254091920 | |
| 1478 | 1479 | }, |
| 1479 | 1480 | { |
| 1480 | 1481 | "id": "324b84ebc607", |
| ... | ... | @@ -1594,7 +1595,7 @@ |
| 1594 | 1595 | "likes": 0, |
| 1595 | 1596 | "createdAt": 1788008156307, |
| 1596 | 1597 | "promptEn": "hotel floor sealed by the Eternity Devil, an endless looping corridor", |
| 1597 | - "lastPlayedAt": 1788251948919 | |
| 1598 | + "lastPlayedAt": 1788254119932 | |
| 1598 | 1599 | }, |
| 1599 | 1600 | { |
| 1600 | 1601 | "id": "cd3b6dda90ad", |
| ... | ... | @@ -1654,7 +1655,7 @@ |
| 1654 | 1655 | "likes": 0, |
| 1655 | 1656 | "createdAt": 1788007201908, |
| 1656 | 1657 | "promptEn": "breaking into the palace, Neferpitou stalling to protect the king", |
| 1657 | - "lastPlayedAt": 1788252681196 | |
| 1658 | + "lastPlayedAt": 1788254139946 | |
| 1658 | 1659 | }, |
| 1659 | 1660 | { |
| 1660 | 1661 | "id": "628026375795", |
| ... | ... | @@ -1699,7 +1700,7 @@ |
| 1699 | 1700 | "likes": 0, |
| 1700 | 1701 | "createdAt": 1788006252918, |
| 1701 | 1702 | "promptEn": "facing L, stayed passive and the evidence pressure maxed out", |
| 1702 | - "lastPlayedAt": 1788253113430 | |
| 1703 | + "lastPlayedAt": 1788254350039 | |
| 1703 | 1704 | }, |
| 1704 | 1705 | { |
| 1705 | 1706 | "id": "0e28a7fecf03", |
| ... | ... | @@ -1939,7 +1940,7 @@ |
| 1939 | 1940 | "likes": 0, |
| 1940 | 1941 | "createdAt": 1788002929455, |
| 1941 | 1942 | "promptEn": "facing Reiner, exploiting a flaw to counter him", |
| 1942 | - "lastPlayedAt": 1788252195030 | |
| 1943 | + "lastPlayedAt": 1788254288010 | |
| 1943 | 1944 | }, |
| 1944 | 1945 | { |
| 1945 | 1946 | "id": "ea8b38014674", |
| ... | ... | @@ -1954,7 +1955,7 @@ |
| 1954 | 1955 | "likes": 0, |
| 1955 | 1956 | "createdAt": 1788002691822, |
| 1956 | 1957 | "promptEn": "facing Reiner, held the goal but paid a price", |
| 1957 | - "lastPlayedAt": 1788252655237 | |
| 1958 | + "lastPlayedAt": 1788254037898 | |
| 1958 | 1959 | }, |
| 1959 | 1960 | { |
| 1960 | 1961 | "id": "f65b15d67966", |
| ... | ... | @@ -2059,7 +2060,7 @@ |
| 2059 | 2060 | "likes": 0, |
| 2060 | 2061 | "createdAt": 1788001049233, |
| 2061 | 2062 | "promptEn": "facing Pain, held the goal but paid a price", |
| 2062 | - "lastPlayedAt": 1788251780839 | |
| 2063 | + "lastPlayedAt": 1788254115931 | |
| 2063 | 2064 | }, |
| 2064 | 2065 | { |
| 2065 | 2066 | "id": "7ecd480a3c31", |
| ... | ... | @@ -2224,7 +2225,7 @@ |
| 2224 | 2225 | "likes": 0, |
| 2225 | 2226 | "createdAt": 1787900324972, |
| 2226 | 2227 | "promptEn": "constable declines a drink, asks what this place is, she watches him through the screen", |
| 2227 | - "lastPlayedAt": 1788252709206 | |
| 2228 | + "lastPlayedAt": 1788254371882 | |
| 2228 | 2229 | }, |
| 2229 | 2230 | { |
| 2230 | 2231 | "id": "43ee5c728f1b", |
| ... | ... | @@ -2434,7 +2435,7 @@ |
| 2434 | 2435 | "likes": 0, |
| 2435 | 2436 | "createdAt": 1787708713767, |
| 2436 | 2437 | "promptEn": "constable stops looking around, turns to face the screen, speaks to her silhouette", |
| 2437 | - "lastPlayedAt": 1788252811258 | |
| 2438 | + "lastPlayedAt": 1788254322029 | |
| 2438 | 2439 | }, |
| 2439 | 2440 | { |
| 2440 | 2441 | "id": "5dc95a15cd5d", |
| ... | ... | @@ -2464,7 +2465,7 @@ |
| 2464 | 2465 | "likes": 0, |
| 2465 | 2466 | "createdAt": 1787708686397, |
| 2466 | 2467 | "promptEn": "constable accepts the invitation, moves to sit on a stool closer to the screen", |
| 2467 | - "lastPlayedAt": 1788248577063 | |
| 2468 | + "lastPlayedAt": 1788254145947 | |
| 2468 | 2469 | }, |
| 2469 | 2470 | { |
| 2470 | 2471 | "id": "ad611bcbd06b", |
| ... | ... | @@ -2539,7 +2540,7 @@ |
| 2539 | 2540 | "likes": 0, |
| 2540 | 2541 | "createdAt": 1787708648409, |
| 2541 | 2542 | "promptEn": "she reaches a hand through the screen gap beckoning him, he steps back and firmly refuses", |
| 2542 | - "lastPlayedAt": 1788251828861 | |
| 2543 | + "lastPlayedAt": 1788254125936 | |
| 2543 | 2544 | }, |
| 2544 | 2545 | { |
| 2545 | 2546 | "id": "3f0c28f5373a", |
| ... | ... | @@ -2749,7 +2750,7 @@ |
| 2749 | 2750 | "likes": 0, |
| 2750 | 2751 | "createdAt": 1787674688717, |
| 2751 | 2752 | "promptEn": "constable agrees to stay, lets his guard down, she gives a barely-there nod", |
| 2752 | - "lastPlayedAt": 1788250832261 | |
| 2753 | + "lastPlayedAt": 1788254161950 | |
| 2753 | 2754 | }, |
| 2754 | 2755 | { |
| 2755 | 2756 | "id": "4874fe3fdbc9", |
| ... | ... | @@ -2779,7 +2780,7 @@ |
| 2779 | 2780 | "likes": 0, |
| 2780 | 2781 | "createdAt": 1787674642139, |
| 2781 | 2782 | "promptEn": "constable forms hand seals and summons a giant toad out of smoke", |
| 2782 | - "lastPlayedAt": 1788250076972 | |
| 2783 | + "lastPlayedAt": 1788254251995 | |
| 2783 | 2784 | }, |
| 2784 | 2785 | { |
| 2785 | 2786 | "id": "e9a6320e18a9", |
| ... | ... | @@ -2854,7 +2855,7 @@ |
| 2854 | 2855 | "likes": 0, |
| 2855 | 2856 | "createdAt": 1787674446841, |
| 2856 | 2857 | "promptEn": "player panics into the room, glances back, out of breath, says something's chasing him outside", |
| 2857 | - "lastPlayedAt": 1788250856275 | |
| 2858 | + "lastPlayedAt": 1788254185961 | |
| 2858 | 2859 | }, |
| 2859 | 2860 | { |
| 2860 | 2861 | "id": "c25d53a5e086", |
| ... | ... | @@ -2869,7 +2870,7 @@ |
| 2869 | 2870 | "likes": 0, |
| 2870 | 2871 | "createdAt": 1787674395006, |
| 2871 | 2872 | "promptEn": "constable and the woman hold a tense silent stare through the screen", |
| 2872 | - "lastPlayedAt": 1788252897296 | |
| 2873 | + "lastPlayedAt": 1788254099923 | |
| 2873 | 2874 | }, |
| 2874 | 2875 | { |
| 2875 | 2876 | "id": "f264bce22903", |
| ... | ... | @@ -2974,7 +2975,7 @@ |
| 2974 | 2975 | "likes": 0, |
| 2975 | 2976 | "createdAt": 1787640567001, |
| 2976 | 2977 | "promptEn": "summoned toad Wentai, beat the painted-skin female ghost", |
| 2977 | - "lastPlayedAt": 1788252685196 | |
| 2978 | + "lastPlayedAt": 1788254552120 | |
| 2978 | 2979 | }, |
| 2979 | 2980 | { |
| 2980 | 2981 | "id": "1cc42a8669a1", |
| ... | ... | @@ -3004,7 +3005,7 @@ |
| 3004 | 3005 | "likes": 0, |
| 3005 | 3006 | "createdAt": 1787637292741, |
| 3006 | 3007 | "promptEn": "constable bursts in panicked, spots the woman behind the screen", |
| 3007 | - "lastPlayedAt": 1788251872881 | |
| 3008 | + "lastPlayedAt": 1788254566123 | |
| 3008 | 3009 | }, |
| 3009 | 3010 | { |
| 3010 | 3011 | "id": "93c2b84a2a10", |
| ... | ... | @@ -3139,7 +3140,7 @@ |
| 3139 | 3140 | "likes": 0, |
| 3140 | 3141 | "createdAt": 1787588545801, |
| 3141 | 3142 | "promptEn": "game menu UI, character swaps out a mechanical arm", |
| 3142 | - "lastPlayedAt": 1788249159434 | |
| 3143 | + "lastPlayedAt": 1788254406058 | |
| 3143 | 3144 | }, |
| 3144 | 3145 | { |
| 3145 | 3146 | "id": "fe749aa533a0", |
| ... | ... | @@ -3184,7 +3185,7 @@ |
| 3184 | 3185 | "likes": 0, |
| 3185 | 3186 | "createdAt": 1787588544109, |
| 3186 | 3187 | "promptEn": "vampire romance drama trailer, girl wanders into the castle", |
| 3187 | - "lastPlayedAt": 1788250187972 | |
| 3188 | + "lastPlayedAt": 1788254272005 | |
| 3188 | 3189 | }, |
| 3189 | 3190 | { |
| 3190 | 3191 | "id": "7ded7db676d3", |
| ... | ... | @@ -3199,7 +3200,7 @@ |
| 3199 | 3200 | "likes": 0, |
| 3200 | 3201 | "createdAt": 1787588543551, |
| 3201 | 3202 | "promptEn": "MiniMax H3 tech brand film, blue waveform logo", |
| 3202 | - "lastPlayedAt": 1788250954321 | |
| 3203 | + "lastPlayedAt": 1788254047898 | |
| 3203 | 3204 | }, |
| 3204 | 3205 | { |
| 3205 | 3206 | "id": "81711242178d", |
| ... | ... | @@ -3244,7 +3245,7 @@ |
| 3244 | 3245 | "likes": 0, |
| 3245 | 3246 | "createdAt": 1787588541726, |
| 3246 | 3247 | "promptEn": "woman opens the trunk on a desert road, takes out a bag", |
| 3247 | - "lastPlayedAt": 1788251984934 | |
| 3248 | + "lastPlayedAt": 1788254335882 | |
| 3248 | 3249 | }, |
| 3249 | 3250 | { |
| 3250 | 3251 | "id": "97846542ab09", |
| ... | ... | @@ -3514,7 +3515,7 @@ |
| 3514 | 3515 | "likes": 0, |
| 3515 | 3516 | "createdAt": 1787573698485, |
| 3516 | 3517 | "promptEn": "underground rap duo freestyling at the camera", |
| 3517 | - "lastPlayedAt": 1788252777241 | |
| 3518 | + "lastPlayedAt": 1788254075916 | |
| 3518 | 3519 | }, |
| 3519 | 3520 | { |
| 3520 | 3521 | "id": "e5246eae814e", |
| ... | ... | @@ -3543,7 +3544,7 @@ |
| 3543 | 3544 | "partition": "text", |
| 3544 | 3545 | "likes": 0, |
| 3545 | 3546 | "createdAt": 1787568512602, |
| 3546 | - "lastPlayedAt": 1788252082974, | |
| 3547 | + "lastPlayedAt": 1788254065909, | |
| 3547 | 3548 | "promptEn": "man apologizes, says something outside was chasing him" |
| 3548 | 3549 | }, |
| 3549 | 3550 | { |
| ... | ... | @@ -3664,7 +3665,7 @@ |
| 3664 | 3665 | "likes": 0, |
| 3665 | 3666 | "createdAt": 1787470101411, |
| 3666 | 3667 | "promptEn": "vertical family-fight drama, arguing with an older woman", |
| 3667 | - "lastPlayedAt": 1788251276478 | |
| 3668 | + "lastPlayedAt": 1788254011880 | |
| 3668 | 3669 | }, |
| 3669 | 3670 | { |
| 3670 | 3671 | "id": "57169e6751d0", |
| ... | ... | @@ -3754,7 +3755,7 @@ |
| 3754 | 3755 | "likes": 0, |
| 3755 | 3756 | "createdAt": 1787470099306, |
| 3756 | 3757 | "promptEn": "late-night laundromat footage mixed with glowing hand-drawn animation", |
| 3757 | - "lastPlayedAt": 1788251502656 | |
| 3758 | + "lastPlayedAt": 1788254294016 | |
| 3758 | 3759 | }, |
| 3759 | 3760 | { |
| 3760 | 3761 | "id": "c0e23beb4132", |
| ... | ... | @@ -3814,7 +3815,7 @@ |
| 3814 | 3815 | "likes": 0, |
| 3815 | 3816 | "createdAt": 1787455895150, |
| 3816 | 3817 | "promptEn": "protagonist leaps across rooftops fleeing pursuers", |
| 3817 | - "lastPlayedAt": 1788252253059 | |
| 3818 | + "lastPlayedAt": 1788254418063 | |
| 3818 | 3819 | }, |
| 3819 | 3820 | { |
| 3820 | 3821 | "id": "2a4e45820135", |
| ... | ... | @@ -4054,7 +4055,7 @@ |
| 4054 | 4055 | "likes": 0, |
| 4055 | 4056 | "createdAt": 1787413511959, |
| 4056 | 4057 | "promptEn": "protagonist leaps across rooftops fleeing pursuers", |
| 4057 | - "lastPlayedAt": 1788251161159 | |
| 4058 | + "lastPlayedAt": 1788254235982 | |
| 4058 | 4059 | }, |
| 4059 | 4060 | { |
| 4060 | 4061 | "id": "5d4f40bf0889", |
| ... | ... | @@ -4099,7 +4100,7 @@ |
| 4099 | 4100 | "likes": 0, |
| 4100 | 4101 | "createdAt": 1787412150062, |
| 4101 | 4102 | "promptEn": "protagonist leaps across rooftops fleeing pursuers", |
| 4102 | - "lastPlayedAt": 1788252809259 | |
| 4103 | + "lastPlayedAt": 1788254542110 | |
| 4103 | 4104 | }, |
| 4104 | 4105 | { |
| 4105 | 4106 | "id": "9d2486d6f969", |
| ... | ... | @@ -4129,7 +4130,7 @@ |
| 4129 | 4130 | "likes": 0, |
| 4130 | 4131 | "createdAt": 1787388090646, |
| 4131 | 4132 | "promptEn": "a cat sitting on a windowsill", |
| 4132 | - "lastPlayedAt": 1788252393130 | |
| 4133 | + "lastPlayedAt": 1788254205967 | |
| 4133 | 4134 | }, |
| 4134 | 4135 | { |
| 4135 | 4136 | "id": "c551457bc55e", |
| ... | ... | @@ -4159,7 +4160,7 @@ |
| 4159 | 4160 | "likes": 0, |
| 4160 | 4161 | "createdAt": 1787384983124, |
| 4161 | 4162 | "promptEn": "protagonist leaps across rooftops fleeing pursuers", |
| 4162 | - "lastPlayedAt": 1788251892892 | |
| 4163 | + "lastPlayedAt": 1788254354037 | |
| 4163 | 4164 | }, |
| 4164 | 4165 | { |
| 4165 | 4166 | "id": "7753a046ba79", |
| ... | ... | @@ -4294,7 +4295,7 @@ |
| 4294 | 4295 | "likes": 0, |
| 4295 | 4296 | "createdAt": 1787367453839, |
| 4296 | 4297 | "promptEn": "golden portal opens, man in green jacket throws a glowing spear", |
| 4297 | - "lastPlayedAt": 1788252006941 | |
| 4298 | + "lastPlayedAt": 1788254041895 | |
| 4298 | 4299 | }, |
| 4299 | 4300 | { |
| 4300 | 4301 | "id": "8402adae59dc", |
| ... | ... | @@ -4309,7 +4310,7 @@ |
| 4309 | 4310 | "likes": 0, |
| 4310 | 4311 | "createdAt": 1787363956909, |
| 4311 | 4312 | "promptEn": "protagonist leaps across rooftops fleeing pursuers", |
| 4312 | - "lastPlayedAt": 1788250706222 | |
| 4313 | + "lastPlayedAt": 1788254362044 | |
| 4313 | 4314 | }, |
| 4314 | 4315 | { |
| 4315 | 4316 | "id": "5692965402f5", |
| ... | ... | @@ -4399,7 +4400,7 @@ |
| 4399 | 4400 | "likes": 0, |
| 4400 | 4401 | "createdAt": 1787309864037, |
| 4401 | 4402 | "promptEn": "transparent gaming mouse product film, blue and orange lighting", |
| 4402 | - "lastPlayedAt": 1788252567195 | |
| 4403 | + "lastPlayedAt": 1788254219979 | |
| 4403 | 4404 | }, |
| 4404 | 4405 | { |
| 4405 | 4406 | "id": "073f5df6427a", |
| ... | ... | @@ -4549,7 +4550,7 @@ |
| 4549 | 4550 | "likes": 0, |
| 4550 | 4551 | "createdAt": 1787225897650, |
| 4551 | 4552 | "promptEn": "constable burns the beauty portrait with the lamp, the door opens on its own", |
| 4552 | - "lastPlayedAt": 1788253107430 | |
| 4553 | + "lastPlayedAt": 1788254544113 | |
| 4553 | 4554 | }, |
| 4554 | 4555 | { |
| 4555 | 4556 | "id": "ed3663dcda7f", | ... | ... |
server.js
| ... | ... | @@ -175,6 +175,7 @@ function markPlayed(id) { |
| 175 | 175 | const clip = lib.find((c) => c.id === id); |
| 176 | 176 | if (!clip) return; |
| 177 | 177 | clip.lastPlayedAt = Date.now(); |
| 178 | + clip.playCount = (clip.playCount || 0) + 1; | |
| 178 | 179 | saveLibrary(lib); |
| 179 | 180 | } |
| 180 | 181 | |
| ... | ... | @@ -225,7 +226,11 @@ function pickNextClip() { |
| 225 | 226 | // random among everything that isn't on cooldown, no bias toward "fresh". |
| 226 | 227 | const offCooldown = notPrev.filter((c) => !c.lastPlayedAt || now - c.lastPlayedAt > REPEAT_COOLDOWN_MS); |
| 227 | 228 | const pool = offCooldown.length ? offCooldown : notPrev; |
| 228 | - return { clip: pool[Math.floor(Math.random() * pool.length)], requestedBy: null, chatId: null }; | |
| 229 | + // Within that pool, only randomize among the least-played clips — keeps | |
| 230 | + // rotation even instead of letting a few clips dominate airtime by chance. | |
| 231 | + const minPlays = Math.min(...pool.map((c) => c.playCount || 0)); | |
| 232 | + const leastPlayed = pool.filter((c) => (c.playCount || 0) === minPlays); | |
| 233 | + return { clip: leastPlayed[Math.floor(Math.random() * leastPlayed.length)], requestedBy: null, chatId: null }; | |
| 229 | 234 | } |
| 230 | 235 | |
| 231 | 236 | const FAILED_ENTRY_TTL_MS = 20000; // how long a "generation failed" entry stays visible before cleanup | ... | ... |