95 lines
2.5 KiB
TypeScript
95 lines
2.5 KiB
TypeScript
import crypto from 'crypto';
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
|
|
export class ThumbnailManager {
|
|
private static readonly THUMBNAIL_WIDTH = 320;
|
|
private static readonly HASH_ALGORITHM = 'md5';
|
|
private static readonly THUMBNAIL_EXTENSION = '.png';
|
|
|
|
/**
|
|
* Generate MD5 hash for a file path
|
|
*/
|
|
static generateHash(filePath: string): string {
|
|
return crypto
|
|
.createHash(this.HASH_ALGORITHM)
|
|
.update(filePath)
|
|
.digest('hex');
|
|
}
|
|
|
|
/**
|
|
* Generate thumbnail filename based on hash and width
|
|
*/
|
|
static getThumbnailFilename(hash: string, width: number = this.THUMBNAIL_WIDTH): string {
|
|
return `${hash}_${width}${this.THUMBNAIL_EXTENSION}`;
|
|
}
|
|
|
|
/**
|
|
* Get the folder structure for a given hash
|
|
*/
|
|
static getFolderStructure(hash: string): { folder1: string; folder2: string } {
|
|
return {
|
|
folder1: hash.substring(0, 2),
|
|
folder2: hash.substring(2, 4)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get the complete thumbnail path information
|
|
*/
|
|
static getThumbnailPath(filePath: string): {
|
|
folderPath: string;
|
|
filename: string;
|
|
fullPath: string;
|
|
url: string;
|
|
} {
|
|
const hash = this.generateHash(filePath);
|
|
const { folder1, folder2 } = this.getFolderStructure(hash);
|
|
const filename = this.getThumbnailFilename(hash);
|
|
|
|
const folderPath = path.join('thumbnails', folder1, folder2);
|
|
const fullPath = path.join(process.cwd(), 'public', folderPath, filename);
|
|
const url = `/${folderPath}/${filename}`;
|
|
|
|
return { folderPath, filename, fullPath, url };
|
|
}
|
|
|
|
/**
|
|
* Ensure thumbnail directory exists
|
|
*/
|
|
static ensureDirectory(folderPath: string): void {
|
|
const fullPath = path.join(process.cwd(), 'public', folderPath);
|
|
if (!fs.existsSync(fullPath)) {
|
|
fs.mkdirSync(fullPath, { recursive: true });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the base thumbnails directory
|
|
*/
|
|
static getThumbnailsBaseDir(): string {
|
|
return path.join(process.cwd(), 'public', 'thumbnails');
|
|
}
|
|
|
|
/**
|
|
* Check if a thumbnail exists
|
|
*/
|
|
static thumbnailExists(filePath: string): boolean {
|
|
const { fullPath } = this.getThumbnailPath(filePath);
|
|
return fs.existsSync(fullPath);
|
|
}
|
|
|
|
/**
|
|
* Get fallback thumbnail URL based on media type
|
|
*/
|
|
static getFallbackThumbnailUrl(mediaType: 'video' | 'photo' | 'text'): string {
|
|
switch (mediaType) {
|
|
case 'video':
|
|
return '/placeholder-video.svg';
|
|
case 'photo':
|
|
return '/placeholder-photo.svg';
|
|
default:
|
|
return '/placeholder.svg';
|
|
}
|
|
}
|
|
} |