feat: Add logic to refresh thumbnail with random timestamp
This commit is contained in:
@@ -2,10 +2,14 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import * as videoMetadataController from '../../controllers/videoMetadataController';
|
||||
import * as metadataService from '../../services/metadataService';
|
||||
import * as storageService from '../../services/storageService';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('../../services/storageService');
|
||||
vi.mock('../../services/metadataService', () => ({
|
||||
getVideoDuration: vi.fn()
|
||||
}));
|
||||
vi.mock('../../utils/security', () => ({
|
||||
validateVideoPath: vi.fn((path) => path),
|
||||
validateImagePath: vi.fn((path) => path),
|
||||
@@ -108,4 +112,37 @@ describe('videoMetadataController', () => {
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('refreshThumbnail', () => {
|
||||
it('should refresh thumbnail with random timestamp', async () => {
|
||||
mockReq.params = { id: '123' };
|
||||
const mockVideo = {
|
||||
id: '123',
|
||||
videoPath: '/videos/test.mp4',
|
||||
thumbnailPath: '/images/test.jpg',
|
||||
thumbnailFilename: 'test.jpg'
|
||||
};
|
||||
(storageService.getVideoById as any).mockReturnValue(mockVideo);
|
||||
(metadataService.getVideoDuration as any).mockResolvedValue(100); // 100 seconds duration
|
||||
|
||||
await videoMetadataController.refreshThumbnail(mockReq as Request, mockRes as Response);
|
||||
|
||||
expect(storageService.getVideoById).toHaveBeenCalledWith('123');
|
||||
expect(metadataService.getVideoDuration).toHaveBeenCalled();
|
||||
|
||||
// Verify execFileSafe was called with ffmpeg
|
||||
// The exact arguments depend on the random timestamp, but we can verify the structure
|
||||
const security = await import('../../utils/security');
|
||||
expect(security.execFileSafe).toHaveBeenCalledWith(
|
||||
'ffmpeg',
|
||||
expect.arrayContaining([
|
||||
'-i', expect.stringContaining('test.mp4'),
|
||||
'-ss', expect.stringMatching(/^\d{2}:\d{2}:\d{2}$/),
|
||||
'-vframes', '1',
|
||||
expect.stringContaining('test.jpg'),
|
||||
'-y'
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import fs from "fs-extra";
|
||||
import path from "path";
|
||||
import { IMAGES_DIR, VIDEOS_DIR } from "../config/paths";
|
||||
import { NotFoundError, ValidationError } from "../errors/DownloadErrors";
|
||||
import { getVideoDuration } from "../services/metadataService";
|
||||
import * as storageService from "../services/storageService";
|
||||
import { logger } from "../utils/logger";
|
||||
import { successResponse } from "../utils/response";
|
||||
@@ -95,11 +96,31 @@ export const refreshThumbnail = async (
|
||||
const validatedThumbnailPath = validateImagePath(thumbnailAbsolutePath);
|
||||
fs.ensureDirSync(path.dirname(validatedThumbnailPath));
|
||||
|
||||
// Calculate random timestamp
|
||||
let timestamp = "00:00:00";
|
||||
try {
|
||||
const duration = await getVideoDuration(validatedVideoPath);
|
||||
if (duration && duration > 0) {
|
||||
// Pick a random second, avoiding the very beginning and very end if possible
|
||||
// But for simplicity and to match request "random frame", valid random second is fine.
|
||||
// Let's ensure we don't go past the end.
|
||||
const randomSecond = Math.floor(Math.random() * duration);
|
||||
const hours = Math.floor(randomSecond / 3600);
|
||||
const minutes = Math.floor((randomSecond % 3600) / 60);
|
||||
const seconds = randomSecond % 60;
|
||||
timestamp = `${hours.toString().padStart(2, "0")}:${minutes
|
||||
.toString()
|
||||
.padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn("Failed to get video duration for random thumbnail, using default 00:00:00", err);
|
||||
}
|
||||
|
||||
// Generate thumbnail using execFileSafe to prevent command injection
|
||||
try {
|
||||
await execFileSafe("ffmpeg", [
|
||||
"-i", validatedVideoPath,
|
||||
"-ss", "00:00:00",
|
||||
"-ss", timestamp,
|
||||
"-vframes", "1",
|
||||
validatedThumbnailPath,
|
||||
"-y"
|
||||
|
||||
Reference in New Issue
Block a user