Restart repository history from target event
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { execFile } from 'child_process';
|
||||
import util from 'util';
|
||||
import { getSpectrogramsPath, ensureSpectrogramsDir } from '@/lib/utils/paths';
|
||||
import { AnalysisTrack, SpectrogramResponse } from '@/types';
|
||||
|
||||
const execFileAsync = util.promisify(execFile);
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { albumPath, tracks, upc } = await request.json();
|
||||
|
||||
if (!albumPath || !tracks || !Array.isArray(tracks)) {
|
||||
return NextResponse.json({ error: 'Invalid request' }, { status: 400 });
|
||||
}
|
||||
|
||||
const specRoot = await ensureSpectrogramsDir();
|
||||
// Use UPC if available, otherwise fallback to album directory name
|
||||
const albumId = upc || path.basename(albumPath);
|
||||
const albumSpecDir = path.join(specRoot, albumId);
|
||||
|
||||
await fs.ensureDir(albumSpecDir);
|
||||
|
||||
const results: Record<string, { exists: boolean, url?: string }> = {};
|
||||
|
||||
// Process tracks
|
||||
// We use Promise.all to process in parallel.
|
||||
// If this causes performance issues with sox, we might need to limit concurrency.
|
||||
await Promise.all(tracks.map(async (track: AnalysisTrack) => {
|
||||
if ('error' in track) return;
|
||||
|
||||
const trackNo = track.common?.track?.no;
|
||||
if (trackNo === undefined || trackNo === null) return;
|
||||
|
||||
const outputFilename = `${trackNo}.png`;
|
||||
const outputPath = path.join(albumSpecDir, outputFilename);
|
||||
const relativePath = path.join(albumId, outputFilename);
|
||||
const publicUrl = `/api/spectrogram?path=${encodeURIComponent(relativePath)}`;
|
||||
|
||||
const exists = await fs.pathExists(outputPath);
|
||||
|
||||
if (!exists) {
|
||||
// Generate
|
||||
const title = `${trackNo} - ${track.common?.title || track.filename}`;
|
||||
|
||||
const args = [
|
||||
track.path,
|
||||
'-n',
|
||||
];
|
||||
|
||||
if (process.env.GENERATE_MONO_SPECTROGRAMS) {
|
||||
args.push('remix', '1');
|
||||
}
|
||||
|
||||
args.push(
|
||||
'spectrogram',
|
||||
'-t', title,
|
||||
'-o', outputPath
|
||||
);
|
||||
|
||||
try {
|
||||
await execFileAsync('sox', args);
|
||||
results[track.filename] = { exists: true, url: publicUrl };
|
||||
} catch (e) {
|
||||
console.error(`Failed to generate spectrogram for ${track.filename}:`, e);
|
||||
results[track.filename] = { exists: false };
|
||||
}
|
||||
} else {
|
||||
results[track.filename] = { exists: true, url: publicUrl };
|
||||
}
|
||||
}));
|
||||
|
||||
return NextResponse.json({ results });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error generating spectrograms:', error);
|
||||
return NextResponse.json({ error: 'Failed to generate spectrograms' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE() {
|
||||
try {
|
||||
const specPath = getSpectrogramsPath();
|
||||
await fs.remove(specPath);
|
||||
await fs.ensureDir(specPath);
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error purging spectrograms:', error);
|
||||
return NextResponse.json({ error: 'Failed to purge spectrograms' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const relativePath = searchParams.get('path');
|
||||
|
||||
if (!relativePath) {
|
||||
return NextResponse.json({ error: 'Missing path' }, { status: 400 });
|
||||
}
|
||||
|
||||
const specRoot = getSpectrogramsPath();
|
||||
const fullPath = path.join(specRoot, relativePath);
|
||||
|
||||
// Security check: ensure fullPath is within specRoot (prevent directory traversal)
|
||||
const resolvedPath = path.resolve(fullPath);
|
||||
const resolvedRoot = path.resolve(specRoot);
|
||||
|
||||
if (!resolvedPath.startsWith(resolvedRoot)) {
|
||||
return NextResponse.json({ error: 'Access denied' }, { status: 403 });
|
||||
}
|
||||
|
||||
if (!await fs.pathExists(resolvedPath)) {
|
||||
return NextResponse.json({ error: 'File not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const fileBuffer = await fs.readFile(resolvedPath);
|
||||
|
||||
return new NextResponse(fileBuffer, {
|
||||
headers: {
|
||||
'Content-Type': 'image/png',
|
||||
'Cache-Control': 'public, max-age=31536000, immutable',
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user