Spaces:
Sleeping
Sleeping
Filter submitted session clips by current take
Browse files
src/app/api/public/reviews/submit-session/route.ts
CHANGED
|
@@ -11,6 +11,24 @@ const sanitizeText = (value: FormDataEntryValue | null, max = 200) => {
|
|
| 11 |
return value.trim().slice(0, max);
|
| 12 |
};
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
export async function POST(req: NextRequest) {
|
| 15 |
try {
|
| 16 |
const form = await req.formData();
|
|
@@ -20,12 +38,16 @@ export async function POST(req: NextRequest) {
|
|
| 20 |
const deviceKey = sanitizeText(form.get('deviceKey'), 200) || null;
|
| 21 |
const tableId = sanitizeText(form.get('tableId'), 80) || null;
|
| 22 |
const sessionId = sanitizeText(form.get('sessionId'), 100);
|
|
|
|
| 23 |
|
| 24 |
if (!consentAccepted) {
|
| 25 |
return NextResponse.json({ error: 'Consent is required' }, { status: 400 });
|
| 26 |
}
|
| 27 |
|
| 28 |
-
const clips = await listSessionClips(sessionId)
|
|
|
|
|
|
|
|
|
|
| 29 |
const videoClips = clips
|
| 30 |
.filter((clip) => clip.mediaType === 'video')
|
| 31 |
.map((clip) => ({ filePath: clip.filePath, ext: clip.ext }));
|
|
|
|
| 11 |
return value.trim().slice(0, max);
|
| 12 |
};
|
| 13 |
|
| 14 |
+
function collectExpectedClips(form: FormData) {
|
| 15 |
+
const count = Number(sanitizeText(form.get('clipCount'), 10));
|
| 16 |
+
const total = Number.isFinite(count) ? Math.max(0, Math.min(20, count)) : 0;
|
| 17 |
+
const expected = new Map<string, number>();
|
| 18 |
+
|
| 19 |
+
for (let i = 0; i < total; i++) {
|
| 20 |
+
const step = Number(sanitizeText(form.get(`clipStep${i}`), 10));
|
| 21 |
+
const takeId = Number(sanitizeText(form.get(`clipTakeId${i}`), 20));
|
| 22 |
+
const mediaType = sanitizeText(form.get(`clipMediaType${i}`), 12);
|
| 23 |
+
if (!Number.isInteger(step) || step < 1 || step > 20) continue;
|
| 24 |
+
if (!Number.isSafeInteger(takeId) || takeId < 0) continue;
|
| 25 |
+
if (mediaType !== 'video' && mediaType !== 'audio') continue;
|
| 26 |
+
expected.set(`${step}:${mediaType}`, takeId);
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
return expected;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
export async function POST(req: NextRequest) {
|
| 33 |
try {
|
| 34 |
const form = await req.formData();
|
|
|
|
| 38 |
const deviceKey = sanitizeText(form.get('deviceKey'), 200) || null;
|
| 39 |
const tableId = sanitizeText(form.get('tableId'), 80) || null;
|
| 40 |
const sessionId = sanitizeText(form.get('sessionId'), 100);
|
| 41 |
+
const expectedClips = collectExpectedClips(form);
|
| 42 |
|
| 43 |
if (!consentAccepted) {
|
| 44 |
return NextResponse.json({ error: 'Consent is required' }, { status: 400 });
|
| 45 |
}
|
| 46 |
|
| 47 |
+
const clips = (await listSessionClips(sessionId)).filter((clip) => {
|
| 48 |
+
if (expectedClips.size === 0) return true;
|
| 49 |
+
return expectedClips.get(`${clip.step}:${clip.mediaType}`) === clip.takeId;
|
| 50 |
+
});
|
| 51 |
const videoClips = clips
|
| 52 |
.filter((clip) => clip.mediaType === 'video')
|
| 53 |
.map((clip) => ({ filePath: clip.filePath, ext: clip.ext }));
|
src/app/preview/page.tsx
CHANGED
|
@@ -122,6 +122,11 @@ export default function PreviewPage() {
|
|
| 122 |
deviceKey: ensureDeviceKey(),
|
| 123 |
tableId: store.tableId,
|
| 124 |
sessionId: store.sessionId,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
});
|
| 126 |
|
| 127 |
setPhase({ kind: 'polling', result });
|
|
|
|
| 122 |
deviceKey: ensureDeviceKey(),
|
| 123 |
tableId: store.tableId,
|
| 124 |
sessionId: store.sessionId,
|
| 125 |
+
clips: clipsToUpload.map((clip) => ({
|
| 126 |
+
step: clip.step,
|
| 127 |
+
takeId: clip.takeId,
|
| 128 |
+
mediaType: clip.mediaType,
|
| 129 |
+
})),
|
| 130 |
});
|
| 131 |
|
| 132 |
setPhase({ kind: 'polling', result });
|
src/lib/humeoApi.ts
CHANGED
|
@@ -85,6 +85,11 @@ export type SubmitSessionInput = Omit<
|
|
| 85 |
'durationSeconds' | 'video' | 'videoFileName'
|
| 86 |
> & {
|
| 87 |
sessionId: string;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
};
|
| 89 |
|
| 90 |
export async function submit(input: SubmitInput): Promise<PublicSubmitResult> {
|
|
@@ -171,6 +176,14 @@ export async function submitSession(input: SubmitSessionInput): Promise<PublicSu
|
|
| 171 |
form.append('deviceKey', input.deviceKey);
|
| 172 |
form.append('sessionId', input.sessionId);
|
| 173 |
if (input.tableId) form.append('tableId', input.tableId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
|
| 175 |
const res = await fetch(endpoint('/api/public/reviews/submit-session'), {
|
| 176 |
method: 'POST',
|
|
|
|
| 85 |
'durationSeconds' | 'video' | 'videoFileName'
|
| 86 |
> & {
|
| 87 |
sessionId: string;
|
| 88 |
+
clips?: Array<{
|
| 89 |
+
step: number;
|
| 90 |
+
takeId: number;
|
| 91 |
+
mediaType: 'video' | 'audio';
|
| 92 |
+
}>;
|
| 93 |
};
|
| 94 |
|
| 95 |
export async function submit(input: SubmitInput): Promise<PublicSubmitResult> {
|
|
|
|
| 176 |
form.append('deviceKey', input.deviceKey);
|
| 177 |
form.append('sessionId', input.sessionId);
|
| 178 |
if (input.tableId) form.append('tableId', input.tableId);
|
| 179 |
+
if (input.clips) {
|
| 180 |
+
form.append('clipCount', String(input.clips.length));
|
| 181 |
+
input.clips.forEach((clip, index) => {
|
| 182 |
+
form.append(`clipStep${index}`, String(clip.step));
|
| 183 |
+
form.append(`clipTakeId${index}`, String(clip.takeId));
|
| 184 |
+
form.append(`clipMediaType${index}`, clip.mediaType);
|
| 185 |
+
});
|
| 186 |
+
}
|
| 187 |
|
| 188 |
const res = await fetch(endpoint('/api/public/reviews/submit-session'), {
|
| 189 |
method: 'POST',
|