169 lines
7.5 KiB
TypeScript
169 lines
7.5 KiB
TypeScript
<<<<<<< HEAD
|
|
import { json, type RequestHandler } from '@sveltejs/kit';
|
|
import fs from 'fs/promises';
|
|
import path from 'path';
|
|
|
|
export const POST: RequestHandler = async ({ request }) => {
|
|
try {
|
|
const data = await request.formData();
|
|
const videoFile = data.get('video') as File | null;
|
|
|
|
if (!videoFile) {
|
|
return json({ success: false, error: '업로드된 파일이 없습니다.' }, { status: 400 });
|
|
}
|
|
|
|
// 1. 오늘 날짜를 YYYYMMDD 형식으로 생성
|
|
const today = new Date();
|
|
const year = today.getFullYear();
|
|
const month = String(today.getMonth() + 1).padStart(2, '0');
|
|
const day = String(today.getDate()).padStart(2, '0');
|
|
const dateFolder = `${year}${month}${day}`;
|
|
|
|
// 2. 최종 저장 경로 설정
|
|
const uploadDir = path.join('/workspace/image/movie', dateFolder);
|
|
const filePath = path.join(uploadDir, videoFile.name);
|
|
|
|
// 3. 날짜 폴더가 없으면 생성
|
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
|
|
// 4. 파일 저장
|
|
const buffer = Buffer.from(await videoFile.arrayBuffer());
|
|
await fs.writeFile(filePath, buffer);
|
|
|
|
console.log(`파일 저장 완료: ${filePath}`);
|
|
|
|
return json({ success: true, filePath });
|
|
} catch (error) {
|
|
console.error('파일 업로드 처리 중 오류:', error);
|
|
return json({ success: false, error: '서버에서 파일 처리 중 오류가 발생했습니다.' }, { status: 500 });
|
|
}
|
|
=======
|
|
// src/routes/api/upload-cut/+server.ts
|
|
|
|
import { error, json } from '@sveltejs/kit';
|
|
import * as fs from 'fs/promises';
|
|
import * as path from 'path';
|
|
import { promisify } from 'util';
|
|
import { exec } from 'child_process';
|
|
import pool from '../../../lib/server/database'; // DB 풀 가져오기
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
export const POST: import('@sveltejs/kit').RequestHandler = async ({ request }) => {
|
|
const formData = await request.formData();
|
|
const videoFile = formData.get('video') as File | null;
|
|
// originalFileName은 현재 로직에서는 직접 사용되지 않지만,
|
|
// 클라이언트에서 보내주므로 일단 받아둡니다. 필요시 로깅 등에 활용할 수 있습니다.
|
|
const originalFileName = formData.get('originalFileName') as string | null;
|
|
|
|
if (!videoFile) {
|
|
throw error(400, '업로드된 파일이 없습니다.');
|
|
}
|
|
|
|
// --- 1. 파일 업로드 및 저장 ---
|
|
const today = new Date();
|
|
const year = today.getFullYear();
|
|
const month = String(today.getMonth() + 1).padStart(2, '0');
|
|
const day = String(today.getDate()).padStart(2, '0');
|
|
const dateFolder = `${year}${month}${day}`;
|
|
|
|
const movieBaseDir = '/workspace/image/movie';
|
|
const movieUploadDir = path.join(movieBaseDir, dateFolder);
|
|
await fs.mkdir(movieUploadDir, { recursive: true });
|
|
|
|
// videoFile.name은 클라이언트에서 생성한 '원본이름_cut-...' 형태의 파일명입니다.
|
|
const cutMovieFileName = videoFile.name;
|
|
const movieFilePath = path.join(movieUploadDir, cutMovieFileName);
|
|
const buffer = Buffer.from(await videoFile.arrayBuffer());
|
|
await fs.writeFile(movieFilePath, buffer);
|
|
console.log(`잘라낸 동영상 저장 완료: ${movieFilePath}`);
|
|
|
|
// --- 데이터베이스 처리 로직 시작 ---
|
|
const client = await pool.connect();
|
|
try {
|
|
await client.query('BEGIN'); // 트랜잭션 시작
|
|
|
|
// --- 2. FFprobe로 TotalFrameCount 계산 및 TumerMovie에 INSERT ---
|
|
const ffprobeCommand = `ffprobe -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 "${movieFilePath}"`;
|
|
const { stdout } = await execAsync(ffprobeCommand);
|
|
const totalFrameCount = parseInt(stdout.trim(), 10);
|
|
|
|
if (isNaN(totalFrameCount)) {
|
|
throw new Error('프레임 수를 계산할 수 없습니다.');
|
|
}
|
|
|
|
const locationNetwork = '192.168.1.50'; // 고정값 또는 환경 변수 사용
|
|
const objectType = 0; // 값은 기존 로직을 따름
|
|
|
|
// TumerMovie에 INSERT 후, 생성된 Index 값을 반환받음
|
|
const movieInsertQuery = `
|
|
INSERT INTO public."TumerMovie" ("LocationNetwork", "LocationFile", "TotalFrameCount", "ObjectType")
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING "Index"
|
|
`;
|
|
const movieInsertRes = await client.query(movieInsertQuery, [locationNetwork, cutMovieFileName, totalFrameCount, objectType]);
|
|
const tumerMovieIndex = movieInsertRes.rows[0].Index;
|
|
console.log(`TumerMovie 저장 완료 (잘라낸 영상), Index: ${tumerMovieIndex}`);
|
|
|
|
// --- 3. FFmpeg로 모든 프레임 추출 ---
|
|
const videoFileNameWithoutExt = cutMovieFileName.split('.').slice(0, -1).join('.');
|
|
const imageRefBaseDir = path.join('/workspace/image/image_ref', videoFileNameWithoutExt);
|
|
const tempFrameDir = path.join(imageRefBaseDir, 'temp_frames');
|
|
await fs.mkdir(tempFrameDir, { recursive: true });
|
|
|
|
const ffmpegCommand = `ffmpeg -i "${movieFilePath}" "${tempFrameDir}/%d.png"`;
|
|
await execAsync(ffmpegCommand);
|
|
console.log(`[${cutMovieFileName}] ${totalFrameCount}개 프레임 추출 완료`);
|
|
|
|
// --- 4. 프레임 재구성 및 TumerDatasetRef 데이터 준비 ---
|
|
const extractedFrames = await fs.readdir(tempFrameDir);
|
|
const datasetRefValues: (string | number)[] = [];
|
|
const queryParams: string[] = [];
|
|
let paramIndex = 1;
|
|
|
|
for (const frameFile of extractedFrames) {
|
|
const frameNumber = parseInt(path.basename(frameFile, '.png'), 10);
|
|
const shardDirNumber = Math.floor((frameNumber - 1) / 1000);
|
|
const finalDir = path.join(imageRefBaseDir, String(shardDirNumber));
|
|
await fs.mkdir(finalDir, { recursive: true });
|
|
|
|
const oldPath = path.join(tempFrameDir, frameFile);
|
|
const newPath = path.join(finalDir, `${frameNumber}.png`);
|
|
await fs.rename(oldPath, newPath);
|
|
|
|
const dbLocationFile = path.join('image', 'image_ref', videoFileNameWithoutExt, String(shardDirNumber), `${frameNumber}.png`);
|
|
|
|
queryParams.push(`($${paramIndex++}, $${paramIndex++}, $${paramIndex++})`);
|
|
datasetRefValues.push(tumerMovieIndex, frameNumber, dbLocationFile);
|
|
}
|
|
await fs.rm(tempFrameDir, { recursive: true, force: true });
|
|
console.log(`[${cutMovieFileName}] 프레임 파일 재구성 완료`);
|
|
|
|
// --- 5. TumerDatasetRef에 대량 INSERT ---
|
|
if (datasetRefValues.length > 0) {
|
|
const datasetRefQuery = `
|
|
INSERT INTO public."TumerDatasetRef" ("IndexMovie", "DataNumber", "LocationFile")
|
|
VALUES ${queryParams.join(', ')}
|
|
`;
|
|
await client.query(datasetRefQuery, datasetRefValues);
|
|
console.log(`[${cutMovieFileName}] ${extractedFrames.length}개 프레임 정보 DB 저장 완료`);
|
|
}
|
|
|
|
await client.query('COMMIT'); // 모든 작업 성공 시 트랜잭션 커밋
|
|
|
|
return json({
|
|
success: true,
|
|
message: '잘라낸 영상의 업로드, 프레임 추출 및 DB 저장이 모두 완료되었습니다.',
|
|
tumerMovieIndex,
|
|
totalFrameCount
|
|
});
|
|
|
|
} catch (err) {
|
|
await client.query('ROLLBACK'); // 오류 발생 시 트랜잭션 롤백
|
|
console.error('잘라낸 영상 처리 중 오류 발생:', err);
|
|
throw error(500, '서버 처리 중 오류가 발생했습니다.');
|
|
} finally {
|
|
client.release(); // 클라이언트 연결 해제
|
|
}
|
|
>>>>>>> 0bab142 (add login/logout)
|
|
}; |