Skip to content

Commit c9aaf04

Browse files
authored
[Fix] 썸네일 기본 이미지 설정
[Fix] 썸네일 기본 이미지 설정
2 parents 1d3b9f9 + 0b91930 commit c9aaf04

File tree

9 files changed

+72
-2
lines changed

9 files changed

+72
-2
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface IRecordClient {
2+
post<T>(path: string, data: any): Promise<T>;
3+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { ConfigService } from '@nestjs/config';
3+
import { ErrorStatus } from '../responses/exceptions/errorStatus';
4+
import { HttpService } from '@nestjs/axios';
5+
import { CustomWsException } from '../responses/exceptions/custom-ws.exception';
6+
import { IRecordClient } from './record-client.interface';
7+
8+
@Injectable()
9+
export class RecordClient implements IRecordClient {
10+
constructor(private readonly httpService: HttpService, private readonly configService: ConfigService) {}
11+
12+
private readonly baseUrl = this.configService.get<string>('RECORD_SERVER_URL');
13+
14+
async post<T>(path: string, data: any): Promise<T> {
15+
try {
16+
const response = await this.httpService.post<T>(`${this.baseUrl}${path}`, data).toPromise();
17+
return response.data;
18+
} catch (error) {
19+
throw this.handleError(error);
20+
}
21+
}
22+
23+
// TODO: Error custom 필요
24+
private handleError(_error: any): Error {
25+
throw new CustomWsException(ErrorStatus.RECORD_SERVER_ERROR);
26+
}
27+
}

apps/media/src/common/responses/exceptions/errorStatus.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ export class ErrorStatus {
1010

1111
static readonly API_SERVER_ERROR = new ErrorStatus(500, 'COMMON_5001', 'API 서비스 로직에서 문제가 발생했습니다.');
1212

13+
static readonly RECORD_SERVER_ERROR = new ErrorStatus(
14+
500,
15+
'COMMON_5002',
16+
'RECORD 서비스 로직에서 문제가 발생했습니다.',
17+
);
18+
1319
// User Errors
1420
static readonly USER_NOT_FOUND = new ErrorStatus(404, 'MEMBER_4000', '사용자를 찾을 수 없습니다.');
1521

apps/media/src/sfu/sfu.module.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ConsumerService } from './services/consumer.service';
1010
import { BroadcastModule } from '../broadcast/broadcast.module';
1111
import { ClientService } from './services/client.service';
1212
import { RecordService } from './services/record.service';
13+
import { RecordClient } from 'src/common/clients/record.client';
1314

1415
@Module({
1516
imports: [BroadcastModule, HttpModule],
@@ -23,6 +24,10 @@ import { RecordService } from './services/record.service';
2324
ConsumerService,
2425
RecordService,
2526
ClientService,
27+
{
28+
provide: 'RECORD_CLIENT',
29+
useClass: RecordClient,
30+
},
2631
],
2732
})
2833
export class SfuModule {}

apps/media/src/sfu/sfu.service.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ConfigService } from '@nestjs/config';
2-
import { Injectable } from '@nestjs/common';
2+
import { Inject, Injectable } from '@nestjs/common';
33
import * as mediasoup from 'mediasoup';
44
import { ConnectTransportDto } from './dto/transport-params.interface';
55
import { RoomService } from './services/room.service';
@@ -15,6 +15,7 @@ import { ClientService } from './services/client.service';
1515
import { RecordService } from './services/record.service';
1616
import { User } from '../types/user';
1717
import { SetVideoQualityDto } from './dto/set-video-quality.dto';
18+
import { IRecordClient } from 'src/common/clients/record-client.interface';
1819

1920
@Injectable()
2021
export class SfuService {
@@ -27,10 +28,15 @@ export class SfuService {
2728
private readonly recordService: RecordService,
2829
private readonly clientService: ClientService,
2930
private readonly configService: ConfigService,
31+
@Inject('RECORD_CLIENT')
32+
private readonly recordClient: IRecordClient,
3033
) {}
3134

3235
async createRoom(clientId: string, user: User) {
3336
const room = await this.roomService.createRoom();
37+
38+
await this.recordClient.post('/thumbnail', { roomId: room.id });
39+
3440
const thumbnail = `${this.configService.get('PUBLIC_RECORD_SERVER_URL')}/statics/thumbnails/${room.id}.jpg`;
3541
await this.broadcasterService.createBroadcast(
3642
CreateBroadcastDto.of(room.id, `${user.camperId}님의 방송`, thumbnail, user.id),

apps/record/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ WORKDIR /app
1010

1111
RUN mkdir -p /app/assets/records /app/assets/thumbnails && chmod -R 777 /app/assets
1212

13+
COPY ./apps/record/public/default-thumbnail.jpg /app/apps/record/assets/default-thumbnail.jpg
14+
RUN chmod 644 /app/apps/record/assets/default-thumbnail.jpg
15+
1316
# Environment variables
1417
ARG RECORD_PORT
1518
ARG NCLOUD_ACCESS_KEY
235 KB
Loading

apps/record/src/ffmpeg.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,11 @@ const thumbnailArgs = (dirPath: string, roomId: string) => {
8888
'pipe:0', // SDP를 파이프로 전달
8989
'-vf',
9090
'fps=1/10,scale=1280:720', // 10초마다 한 프레임을 캡처하고 해상도 조정
91+
'-f',
92+
'image2', // 이미지 출력 포맷 명시
9193
'-update',
92-
'1', // 같은 파일에 덮어쓰기 활성화
94+
'1',
95+
'-y', // 강제 덮어쓰기
9396
`${dirPath}/thumbnails/${roomId}.jpg`, // 덮어쓸 출력 파일 이름
9497
];
9598
return commandArgs;

apps/record/src/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const app = express();
1212
const assetsDirPath = path.join(__dirname, '../assets');
1313
const thumbnailsDirPath = path.join(__dirname, '../assets/thumbnails');
1414
const recordsDirPath = path.join(__dirname, '../assets/records');
15+
const defaultThumbnailPath = path.join(__dirname, '../assets/default-thumbnail.jpg');
1516

1617
app.use(express.json());
1718
app.use(cors());
@@ -29,6 +30,22 @@ if (!fs.existsSync(recordsDirPath)) {
2930
fs.mkdirSync(recordsDirPath, { recursive: true });
3031
}
3132

33+
app.post('/thumbnail', (req, res) => {
34+
const { roomId } = req.body;
35+
try {
36+
if (!fs.existsSync(defaultThumbnailPath)) {
37+
console.error('Default thumbnail not found');
38+
res.status(404).send({ error: 'Default thumbnail not found' });
39+
}
40+
const newThumbnailPath = path.join(thumbnailsDirPath, `${roomId}.jpg`);
41+
fs.copyFileSync(defaultThumbnailPath, newThumbnailPath);
42+
console.log('Default Thumbnail 생성됨:', newThumbnailPath);
43+
res.send({ success: true });
44+
} catch (error) {
45+
res.status(500).send({ success: false, error: 'Failed to create thumbnail' });
46+
}
47+
});
48+
3249
app.post('/send', (req, res) => {
3350
const { videoPort, roomId, type, audioPort } = req.body;
3451
createFfmpegProcess(videoPort, assetsDirPath, roomId, type, audioPort);

0 commit comments

Comments
 (0)