aimusic-attribution / server /localStorage.ts
emresar's picture
Upload folder using huggingface_hub
6678fa1 verified
/**
* Local File Storage
*
* Stores uploaded files locally in the uploads/ directory.
* Files are served via Express static middleware.
*/
import fs from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";
import { nanoid } from "nanoid";
// Get directory name in ESM
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Base directory for uploads (relative to project root)
const UPLOADS_DIR = path.resolve(__dirname, "../uploads");
// Subdirectories
const TRACKS_DIR = path.join(UPLOADS_DIR, "tracks");
const STEMS_DIR = path.join(UPLOADS_DIR, "stems");
/**
* Ensure upload directories exist
*/
export async function ensureUploadDirs(): Promise<void> {
await fs.mkdir(TRACKS_DIR, { recursive: true });
await fs.mkdir(STEMS_DIR, { recursive: true });
}
/**
* Get the uploads base directory path
*/
export function getUploadsDir(): string {
return UPLOADS_DIR;
}
/**
* Save an uploaded track file
*
* @param userId User ID
* @param fileName Original filename
* @param data File data as Buffer
* @returns Object with fileKey (relative path) and filePath (absolute path)
*/
export async function saveTrackFile(
userId: number,
fileName: string,
data: Buffer
): Promise<{ fileKey: string; filePath: string; fileUrl: string }> {
await ensureUploadDirs();
// Create user-specific directory
const userDir = path.join(TRACKS_DIR, String(userId));
await fs.mkdir(userDir, { recursive: true });
// Generate unique filename
const ext = path.extname(fileName) || ".mp3";
const uniqueName = `${nanoid(16)}${ext}`;
const filePath = path.join(userDir, uniqueName);
// Write file
await fs.writeFile(filePath, data);
// Generate relative key for database storage
const fileKey = `tracks/${userId}/${uniqueName}`;
// URL path for serving (will be served via /uploads/ route)
const fileUrl = `/uploads/${fileKey}`;
return { fileKey, filePath, fileUrl };
}
/**
* Save a stem file from ML processing
*
* @param trackId Track ID
* @param stemType Stem type (vocals, drums, bass, other)
* @param sourcePath Source file path from ML output
* @returns Object with fileKey, filePath, and fileUrl
*/
export async function saveStemFile(
trackId: number,
stemType: string,
sourcePath: string
): Promise<{ fileKey: string; filePath: string; fileUrl: string }> {
await ensureUploadDirs();
// Create track-specific stems directory
const trackStemsDir = path.join(STEMS_DIR, String(trackId));
await fs.mkdir(trackStemsDir, { recursive: true });
// Get extension from source
const ext = path.extname(sourcePath) || ".mp3";
const uniqueName = `${stemType}-${nanoid(8)}${ext}`;
const filePath = path.join(trackStemsDir, uniqueName);
// Copy file from ML output to uploads directory
await fs.copyFile(sourcePath, filePath);
// Generate relative key for database storage
const fileKey = `stems/${trackId}/${uniqueName}`;
// URL path for serving
const fileUrl = `/uploads/${fileKey}`;
return { fileKey, filePath, fileUrl };
}
/**
* Get the absolute path for a file key
*/
export function getFilePath(fileKey: string): string {
return path.join(UPLOADS_DIR, fileKey);
}
/**
* Check if a file exists
*/
export async function fileExists(fileKey: string): Promise<boolean> {
try {
await fs.access(getFilePath(fileKey));
return true;
} catch {
return false;
}
}
/**
* Delete a file
*/
export async function deleteFile(fileKey: string): Promise<void> {
try {
await fs.unlink(getFilePath(fileKey));
} catch {
// Ignore if file doesn't exist
}
}
/**
* Get file stats
*/
export async function getFileStats(fileKey: string): Promise<{ size: number } | null> {
try {
const stats = await fs.stat(getFilePath(fileKey));
return { size: stats.size };
} catch {
return null;
}
}
/**
* Create a temp directory for ML processing output
*/
export async function createTempDir(prefix: string): Promise<string> {
const tempDir = path.join(UPLOADS_DIR, "temp", `${prefix}-${nanoid(8)}`);
await fs.mkdir(tempDir, { recursive: true });
return tempDir;
}
/**
* Clean up a temp directory
*/
export async function cleanupTempDir(tempDir: string): Promise<void> {
try {
await fs.rm(tempDir, { recursive: true, force: true });
} catch {
// Ignore errors
}
}