mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
feat(server): enhanced thumbnails generation code (#2147)
* Add size parameter to extractVideoThumbnail * Ensure minimum dimension of webp thumbnail
This commit is contained in:
parent
fc585bffcc
commit
7e526f87b4
@ -7,6 +7,6 @@ export interface ResizeOptions {
|
|||||||
|
|
||||||
export interface IMediaRepository {
|
export interface IMediaRepository {
|
||||||
resize(input: string, output: string, options: ResizeOptions): Promise<void>;
|
resize(input: string, output: string, options: ResizeOptions): Promise<void>;
|
||||||
extractVideoThumbnail(input: string, output: string): Promise<void>;
|
extractVideoThumbnail(input: string, output: string, size: number): Promise<void>;
|
||||||
extractThumbnailFromExif(input: string, output: string): Promise<void>;
|
extractThumbnailFromExif(input: string, output: string): Promise<void>;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,7 @@ describe(MediaService.name, () => {
|
|||||||
expect(mediaMock.extractVideoThumbnail).toHaveBeenCalledWith(
|
expect(mediaMock.extractVideoThumbnail).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/thumbs/user-id/asset-id.jpeg',
|
'upload/thumbs/user-id/asset-id.jpeg',
|
||||||
|
1440,
|
||||||
);
|
);
|
||||||
expect(assetMock.save).toHaveBeenCalledWith({
|
expect(assetMock.save).toHaveBeenCalledWith({
|
||||||
id: 'asset-id',
|
id: 'asset-id',
|
||||||
|
@ -44,9 +44,13 @@ export class MediaService {
|
|||||||
this.storageRepository.mkdirSync(resizePath);
|
this.storageRepository.mkdirSync(resizePath);
|
||||||
const jpegThumbnailPath = join(resizePath, `${asset.id}.jpeg`);
|
const jpegThumbnailPath = join(resizePath, `${asset.id}.jpeg`);
|
||||||
|
|
||||||
|
const thumbnailDimension = 1440;
|
||||||
if (asset.type == AssetType.IMAGE) {
|
if (asset.type == AssetType.IMAGE) {
|
||||||
try {
|
try {
|
||||||
await this.mediaRepository.resize(asset.originalPath, jpegThumbnailPath, { size: 1440, format: 'jpeg' });
|
await this.mediaRepository.resize(asset.originalPath, jpegThumbnailPath, {
|
||||||
|
size: thumbnailDimension,
|
||||||
|
format: 'jpeg',
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
`Failed to generate jpeg thumbnail using sharp, trying with exiftool-vendored (asset=${asset.id})`,
|
`Failed to generate jpeg thumbnail using sharp, trying with exiftool-vendored (asset=${asset.id})`,
|
||||||
@ -57,7 +61,7 @@ export class MediaService {
|
|||||||
|
|
||||||
if (asset.type == AssetType.VIDEO) {
|
if (asset.type == AssetType.VIDEO) {
|
||||||
this.logger.log('Start Generating Video Thumbnail');
|
this.logger.log('Start Generating Video Thumbnail');
|
||||||
await this.mediaRepository.extractVideoThumbnail(asset.originalPath, jpegThumbnailPath);
|
await this.mediaRepository.extractVideoThumbnail(asset.originalPath, jpegThumbnailPath, thumbnailDimension);
|
||||||
this.logger.log(`Generating Video Thumbnail Success ${asset.id}`);
|
this.logger.log(`Generating Video Thumbnail Success ${asset.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,11 @@ export class MediaRepository implements IMediaRepository {
|
|||||||
async resize(input: string, output: string, options: ResizeOptions): Promise<void> {
|
async resize(input: string, output: string, options: ResizeOptions): Promise<void> {
|
||||||
switch (options.format) {
|
switch (options.format) {
|
||||||
case 'webp':
|
case 'webp':
|
||||||
await sharp(input, { failOnError: false }).resize(250).webp().rotate().toFile(output);
|
await sharp(input, { failOnError: false })
|
||||||
|
.resize(options.size, options.size, { fit: 'outside', withoutEnlargement: true })
|
||||||
|
.webp()
|
||||||
|
.rotate()
|
||||||
|
.toFile(output);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 'jpeg':
|
case 'jpeg':
|
||||||
@ -24,10 +28,14 @@ export class MediaRepository implements IMediaRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extractVideoThumbnail(input: string, output: string) {
|
extractVideoThumbnail(input: string, output: string, size: number) {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
ffmpeg(input)
|
ffmpeg(input)
|
||||||
.outputOptions(['-ss 00:00:00.000', '-frames:v 1'])
|
.outputOptions([
|
||||||
|
'-ss 00:00:00.000',
|
||||||
|
'-frames:v 1',
|
||||||
|
`-vf scale='min(${size},iw)':'min(${size},ih)':force_original_aspect_ratio=increase`,
|
||||||
|
])
|
||||||
.output(output)
|
.output(output)
|
||||||
.on('error', reject)
|
.on('error', reject)
|
||||||
.on('end', resolve)
|
.on('end', resolve)
|
||||||
|
Loading…
Reference in New Issue
Block a user