8c8a34e320
Add Request.force (migration add_request_force): the pipeline's intake now skips the "already in library" dedupe for a forced job, so an owned album is re-downloaded. The import step still keeps the new copy only when it's higher quality, so a forced upgrade can never downgrade what's on disk. - Worker: _is_force_job → dedupe bypassed when Request.force. - POST /api/library/[id]/upgrade finds the matching MonitoredRelease and enqueues a force request (guards against stacking on an in-flight job). - Library route exposes monitoredReleaseId; the album modal shows a "Replace / upgrade" button when a release exists. Worker + web tests added (pipeline force re-acquire; upgrade route). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
42 lines
1.9 KiB
TypeScript
42 lines
1.9 KiB
TypeScript
import { prisma } from "@/lib/db";
|
|
|
|
// POST /api/library/[id]/upgrade — force a re-acquire of an owned album: enqueue a job that
|
|
// skips the "already in library" dedupe (Request.force). The pipeline downloads the best
|
|
// available copy and the import step keeps it only if it's higher quality, so this can upgrade
|
|
// but never downgrades. Requires a matching MonitoredRelease (scanned/monitored albums have
|
|
// one) to carry the acquisition context.
|
|
export async function POST(_request: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
const { id } = await params;
|
|
const item = await prisma.libraryItem.findUnique({ where: { id } });
|
|
if (!item) return Response.json({ error: "not found" }, { status: 404 });
|
|
|
|
// Prefer the release-group match; fall back to an artist+album match.
|
|
const release =
|
|
(item.rgMbid ? await prisma.monitoredRelease.findUnique({ where: { rgMbid: item.rgMbid } }) : null) ??
|
|
(await prisma.monitoredRelease.findFirst({ where: { artistName: item.artist, album: item.album } }));
|
|
if (!release) {
|
|
return Response.json(
|
|
{ error: "no release info for this album — follow the artist or re-scan to enable upgrades" },
|
|
{ status: 400 },
|
|
);
|
|
}
|
|
|
|
// Don't stack a second job on a release that's already in flight.
|
|
const activeJob = await prisma.job.findFirst({
|
|
where: { request: { monitoredReleaseId: release.id }, state: { notIn: ["imported", "needs_attention"] } },
|
|
});
|
|
if (activeJob) return Response.json({ enqueued: false }, { status: 202 });
|
|
|
|
await prisma.request.create({
|
|
data: {
|
|
artist: release.artistName,
|
|
album: release.album,
|
|
monitoredReleaseId: release.id,
|
|
force: true,
|
|
job: { create: {} },
|
|
},
|
|
});
|
|
await prisma.monitoredRelease.update({ where: { id: release.id }, data: { lastSearchedAt: new Date() } });
|
|
return Response.json({ enqueued: true }, { status: 202 });
|
|
}
|