feat(special-curation): complete implementation with all components

- Database: SpecialSong model with startTime
- Backend: API endpoints for curation
- Admin: Waveform editor and curation page
- Game: startTime support in AudioPlayer
- UI: Curate button in admin dashboard
This commit is contained in:
Hördle Bot
2025-11-23 00:50:35 +01:00
parent 4f088305df
commit 587fa59b79
8 changed files with 564 additions and 23 deletions

View File

@@ -146,32 +146,36 @@ export async function getOrCreateSpecialPuzzle(specialName: string) {
});
if (!dailyPuzzle) {
// Get songs available for this special
const allSongs = await prisma.song.findMany({
where: { specials: { some: { id: special.id } } },
// Get songs available for this special through SpecialSong
const specialSongs = await prisma.specialSong.findMany({
where: { specialId: special.id },
include: {
puzzles: {
where: { specialId: special.id }
},
},
song: {
include: {
puzzles: {
where: { specialId: special.id }
}
}
}
}
});
if (allSongs.length === 0) return null;
if (specialSongs.length === 0) return null;
// Calculate weights
const weightedSongs = allSongs.map(song => ({
song,
weight: 1.0 / (song.puzzles.length + 1),
const weightedSongs = specialSongs.map(specialSong => ({
specialSong,
weight: 1.0 / (specialSong.song.puzzles.length + 1),
}));
const totalWeight = weightedSongs.reduce((sum, item) => sum + item.weight, 0);
let random = Math.random() * totalWeight;
let selectedSong = weightedSongs[0].song;
let selectedSpecialSong = weightedSongs[0].specialSong;
for (const item of weightedSongs) {
random -= item.weight;
if (random <= 0) {
selectedSong = item.song;
selectedSpecialSong = item.specialSong;
break;
}
}
@@ -180,7 +184,7 @@ export async function getOrCreateSpecialPuzzle(specialName: string) {
dailyPuzzle = await prisma.dailyPuzzle.create({
data: {
date: today,
songId: selectedSong.id,
songId: selectedSpecialSong.songId,
specialId: special.id
},
include: { song: true },
@@ -198,6 +202,16 @@ export async function getOrCreateSpecialPuzzle(specialName: string) {
if (!dailyPuzzle) return null;
// Fetch the startTime from SpecialSong
const specialSong = await prisma.specialSong.findUnique({
where: {
specialId_songId: {
specialId: special.id,
songId: dailyPuzzle.songId
}
}
});
// Calculate puzzle number
const puzzleCount = await prisma.dailyPuzzle.count({
where: {
@@ -218,7 +232,8 @@ export async function getOrCreateSpecialPuzzle(specialName: string) {
coverImage: dailyPuzzle.song.coverImage ? `/uploads/covers/${dailyPuzzle.song.coverImage}` : null,
special: specialName,
maxAttempts: special.maxAttempts,
unlockSteps: JSON.parse(special.unlockSteps)
unlockSteps: JSON.parse(special.unlockSteps),
startTime: specialSong?.startTime || 0
};
} catch (error) {