nextav/src/lib/thumbnails.ts

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';
}
}
}