Harden media health checks and duration extraction
فشلت بعض الفحوصات
Deploy To Ghaymah / deploy (push) Has been cancelled

هذا الالتزام موجود في:
boutmoun123
2026-05-31 18:53:07 +03:00
الأصل 1973b8b904
التزام 637782aed6
11 ملفات معدلة مع 587 إضافات و138 حذوفات

عرض الملف

@@ -1,12 +1,9 @@
import {
BadGatewayException,
Injectable,
ServiceUnavailableException,
} from '@nestjs/common';
import { BadGatewayException, Injectable, ServiceUnavailableException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { GoogleAuth } from 'google-auth-library';
import { generateWaveformPeaksFromBuffer } from '../../common/utils/waveform.util';
import { ManagedStorageService } from '../../infrastructure/storage/managed-storage.service';
import { MediaProbeService } from '../../infrastructure/storage/media-probe.service';
import { TextToMusicDto } from './dto/text-to-music.dto';
@Injectable()
@@ -14,29 +11,84 @@ export class MediaService {
constructor(
private readonly configService: ConfigService,
private readonly storageService: ManagedStorageService,
private readonly mediaProbeService: MediaProbeService,
) {}
async getMediaHealth() {
const [storageHealth, ffmpeg, ffprobe] = await Promise.all([
this.storageService.getHealth(),
this.mediaProbeService.checkFfmpeg(),
this.mediaProbeService.checkFfprobe(),
]);
const storageProvider =
this.configService.get<string>('storage.provider', { infer: true }) ?? 'local';
const publicBaseUrl = this.configService.get<string>('publicBaseUrl', { infer: true }) ?? '';
const warnings: string[] = [];
const imageProcessingEnabled =
this.configService.get<boolean>('imageProcessing.enabled', { infer: true }) ?? false;
const videoProcessingEnabled =
this.configService.get<boolean>('videoProcessing.enabled', { infer: true }) ?? false;
const videoHlsGenerationEnabled =
this.configService.get<boolean>('videoProcessing.generateHls', { infer: true }) ?? true;
const videoThumbnailGenerationEnabled =
this.configService.get<boolean>('videoProcessing.generateThumbnails', { infer: true }) ??
true;
const audioProcessingEnabled =
this.configService.get<boolean>('audioProcessing.enabled', { infer: true }) ?? false;
if (storageProvider === 'local') {
warnings.push(
'Local storage requires persistent volume mounted to /app/uploads in production',
);
}
if (!publicBaseUrl) {
warnings.push('PUBLIC_BASE_URL is not configured');
}
if ((imageProcessingEnabled || videoProcessingEnabled) && !ffmpeg.available) {
warnings.push('ffmpeg is not available; image/video processing may fail');
}
if (!ffprobe.available) {
warnings.push('ffprobe is not available; duration extraction may fail');
}
if (storageProvider === 's3' && !(storageHealth.isS3Configured ?? storageHealth.s3Configured)) {
warnings.push('S3 provider selected but missing required env variables');
}
const storageWritable =
storageProvider !== 'local' || storageHealth.uploadPathWritable !== false;
const status = !storageWritable ? 'error' : warnings.length ? 'warning' : 'ok';
return {
storage: await this.storageService.getHealth(),
status,
storage: storageHealth,
processing: {
imageProcessingEnabled:
this.configService.get<boolean>('imageProcessing.enabled', { infer: true }) ?? false,
videoProcessingEnabled:
this.configService.get<boolean>('videoProcessing.enabled', { infer: true }) ?? false,
videoHlsGenerationEnabled:
this.configService.get<boolean>('videoProcessing.generateHls', { infer: true }) ?? true,
videoThumbnailGenerationEnabled:
this.configService.get<boolean>('videoProcessing.generateThumbnails', { infer: true }) ??
true,
ffmpegPath:
this.configService.get<string>('videoProcessing.ffmpegPath', { infer: true }) ?? 'ffmpeg',
imageProcessingEnabled,
videoProcessingEnabled,
videoHlsGenerationEnabled,
videoThumbnailGenerationEnabled,
audioProcessingEnabled,
ffmpegPath: ffmpeg.path,
ffmpegAvailable: ffmpeg.available,
ffmpegVersion: ffmpeg.version,
ffprobePath: ffprobe.path,
ffprobeAvailable: ffprobe.available,
ffprobeVersion: ffprobe.version,
ffmpeg,
ffprobe,
},
serving: {
rangeRequests: true,
immutableCacheSeconds: 31536000,
hlsManifestCacheSeconds: 300,
},
staticServing: {
uploadsPublicPath:
storageHealth.uploadsPublicPath ?? storageHealth.publicPath ?? '/uploads',
rangeRequestsExpected: true,
cacheHeadersExpected: true,
hlsMimeExpected: 'application/vnd.apple.mpegurl',
},
warnings,
};
}
@@ -67,7 +119,7 @@ export class MediaService {
const client = await auth.getClient();
const accessTokenRaw = await client.getAccessToken();
const accessToken =
typeof accessTokenRaw === 'string' ? accessTokenRaw : accessTokenRaw?.token ?? '';
typeof accessTokenRaw === 'string' ? accessTokenRaw : (accessTokenRaw?.token ?? '');
if (!accessToken) {
throw new ServiceUnavailableException('Failed to authenticate with Google Cloud');