mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	refactor: migrate media repository (#15536)
This commit is contained in:
		
							parent
							
								
									30b8864d2d
								
							
						
					
					
						commit
						66849d0d45
					
				@ -12,7 +12,7 @@ import {
 | 
				
			|||||||
  VideoContainer,
 | 
					  VideoContainer,
 | 
				
			||||||
} from 'src/enum';
 | 
					} from 'src/enum';
 | 
				
			||||||
import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
 | 
					import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
 | 
				
			||||||
import { ImageOptions } from 'src/interfaces/media.interface';
 | 
					import { ImageOptions } from 'src/types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface SystemConfig {
 | 
					export interface SystemConfig {
 | 
				
			||||||
  backup: {
 | 
					  backup: {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,151 +0,0 @@
 | 
				
			|||||||
import { Writable } from 'node:stream';
 | 
					 | 
				
			||||||
import { ExifOrientation, ImageFormat, TranscodeTarget, VideoCodec } from 'src/enum';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const IMediaRepository = 'IMediaRepository';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface CropOptions {
 | 
					 | 
				
			||||||
  top: number;
 | 
					 | 
				
			||||||
  left: number;
 | 
					 | 
				
			||||||
  width: number;
 | 
					 | 
				
			||||||
  height: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface ImageOptions {
 | 
					 | 
				
			||||||
  format: ImageFormat;
 | 
					 | 
				
			||||||
  quality: number;
 | 
					 | 
				
			||||||
  size: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface RawImageInfo {
 | 
					 | 
				
			||||||
  width: number;
 | 
					 | 
				
			||||||
  height: number;
 | 
					 | 
				
			||||||
  channels: 1 | 2 | 3 | 4;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
interface DecodeImageOptions {
 | 
					 | 
				
			||||||
  colorspace: string;
 | 
					 | 
				
			||||||
  crop?: CropOptions;
 | 
					 | 
				
			||||||
  processInvalidImages: boolean;
 | 
					 | 
				
			||||||
  raw?: RawImageInfo;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface DecodeToBufferOptions extends DecodeImageOptions {
 | 
					 | 
				
			||||||
  size: number;
 | 
					 | 
				
			||||||
  orientation?: ExifOrientation;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type GenerateThumbnailOptions = ImageOptions & DecodeImageOptions;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type GenerateThumbnailFromBufferOptions = GenerateThumbnailOptions & { raw: RawImageInfo };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type GenerateThumbhashOptions = DecodeImageOptions;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type GenerateThumbhashFromBufferOptions = GenerateThumbhashOptions & { raw: RawImageInfo };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface GenerateThumbnailsOptions {
 | 
					 | 
				
			||||||
  colorspace: string;
 | 
					 | 
				
			||||||
  crop?: CropOptions;
 | 
					 | 
				
			||||||
  preview?: ImageOptions;
 | 
					 | 
				
			||||||
  processInvalidImages: boolean;
 | 
					 | 
				
			||||||
  thumbhash?: boolean;
 | 
					 | 
				
			||||||
  thumbnail?: ImageOptions;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface VideoStreamInfo {
 | 
					 | 
				
			||||||
  index: number;
 | 
					 | 
				
			||||||
  height: number;
 | 
					 | 
				
			||||||
  width: number;
 | 
					 | 
				
			||||||
  rotation: number;
 | 
					 | 
				
			||||||
  codecName?: string;
 | 
					 | 
				
			||||||
  frameCount: number;
 | 
					 | 
				
			||||||
  isHDR: boolean;
 | 
					 | 
				
			||||||
  bitrate: number;
 | 
					 | 
				
			||||||
  pixelFormat: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface AudioStreamInfo {
 | 
					 | 
				
			||||||
  index: number;
 | 
					 | 
				
			||||||
  codecName?: string;
 | 
					 | 
				
			||||||
  frameCount: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface VideoFormat {
 | 
					 | 
				
			||||||
  formatName?: string;
 | 
					 | 
				
			||||||
  formatLongName?: string;
 | 
					 | 
				
			||||||
  duration: number;
 | 
					 | 
				
			||||||
  bitrate: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface ImageDimensions {
 | 
					 | 
				
			||||||
  width: number;
 | 
					 | 
				
			||||||
  height: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface InputDimensions extends ImageDimensions {
 | 
					 | 
				
			||||||
  inputPath: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface VideoInfo {
 | 
					 | 
				
			||||||
  format: VideoFormat;
 | 
					 | 
				
			||||||
  videoStreams: VideoStreamInfo[];
 | 
					 | 
				
			||||||
  audioStreams: AudioStreamInfo[];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface TranscodeCommand {
 | 
					 | 
				
			||||||
  inputOptions: string[];
 | 
					 | 
				
			||||||
  outputOptions: string[];
 | 
					 | 
				
			||||||
  twoPass: boolean;
 | 
					 | 
				
			||||||
  progress: {
 | 
					 | 
				
			||||||
    frameCount: number;
 | 
					 | 
				
			||||||
    percentInterval: number;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface BitrateDistribution {
 | 
					 | 
				
			||||||
  max: number;
 | 
					 | 
				
			||||||
  target: number;
 | 
					 | 
				
			||||||
  min: number;
 | 
					 | 
				
			||||||
  unit: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface ImageBuffer {
 | 
					 | 
				
			||||||
  data: Buffer;
 | 
					 | 
				
			||||||
  info: RawImageInfo;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface VideoCodecSWConfig {
 | 
					 | 
				
			||||||
  getCommand(
 | 
					 | 
				
			||||||
    target: TranscodeTarget,
 | 
					 | 
				
			||||||
    videoStream: VideoStreamInfo,
 | 
					 | 
				
			||||||
    audioStream: AudioStreamInfo,
 | 
					 | 
				
			||||||
    format?: VideoFormat,
 | 
					 | 
				
			||||||
  ): TranscodeCommand;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface VideoCodecHWConfig extends VideoCodecSWConfig {
 | 
					 | 
				
			||||||
  getSupportedCodecs(): Array<VideoCodec>;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface ProbeOptions {
 | 
					 | 
				
			||||||
  countFrames: boolean;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface VideoInterfaces {
 | 
					 | 
				
			||||||
  dri: string[];
 | 
					 | 
				
			||||||
  mali: boolean;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface IMediaRepository {
 | 
					 | 
				
			||||||
  // image
 | 
					 | 
				
			||||||
  extract(input: string, output: string): Promise<boolean>;
 | 
					 | 
				
			||||||
  decodeImage(input: string, options: DecodeToBufferOptions): Promise<ImageBuffer>;
 | 
					 | 
				
			||||||
  generateThumbnail(input: string, options: GenerateThumbnailOptions, outputFile: string): Promise<void>;
 | 
					 | 
				
			||||||
  generateThumbnail(input: Buffer, options: GenerateThumbnailFromBufferOptions, outputFile: string): Promise<void>;
 | 
					 | 
				
			||||||
  generateThumbhash(input: string, options: GenerateThumbhashOptions): Promise<Buffer>;
 | 
					 | 
				
			||||||
  generateThumbhash(input: Buffer, options: GenerateThumbhashFromBufferOptions): Promise<Buffer>;
 | 
					 | 
				
			||||||
  getImageDimensions(input: string): Promise<ImageDimensions>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // video
 | 
					 | 
				
			||||||
  probe(input: string, options?: ProbeOptions): Promise<VideoInfo>;
 | 
					 | 
				
			||||||
  transcode(input: string, output: string | Writable, command: TranscodeCommand): Promise<void>;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -10,7 +10,6 @@ import { ILibraryRepository } from 'src/interfaces/library.interface';
 | 
				
			|||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
					import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
				
			||||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
 | 
					import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
 | 
				
			||||||
import { IMapRepository } from 'src/interfaces/map.interface';
 | 
					import { IMapRepository } from 'src/interfaces/map.interface';
 | 
				
			||||||
import { IMediaRepository } from 'src/interfaces/media.interface';
 | 
					 | 
				
			||||||
import { IMetadataRepository } from 'src/interfaces/metadata.interface';
 | 
					import { IMetadataRepository } from 'src/interfaces/metadata.interface';
 | 
				
			||||||
import { IMoveRepository } from 'src/interfaces/move.interface';
 | 
					import { IMoveRepository } from 'src/interfaces/move.interface';
 | 
				
			||||||
import { INotificationRepository } from 'src/interfaces/notification.interface';
 | 
					import { INotificationRepository } from 'src/interfaces/notification.interface';
 | 
				
			||||||
@ -77,6 +76,7 @@ export const repositories = [
 | 
				
			|||||||
  AuditRepository,
 | 
					  AuditRepository,
 | 
				
			||||||
  ApiKeyRepository,
 | 
					  ApiKeyRepository,
 | 
				
			||||||
  ConfigRepository,
 | 
					  ConfigRepository,
 | 
				
			||||||
 | 
					  MediaRepository,
 | 
				
			||||||
  MemoryRepository,
 | 
					  MemoryRepository,
 | 
				
			||||||
  ViewRepository,
 | 
					  ViewRepository,
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
@ -94,7 +94,6 @@ export const providers = [
 | 
				
			|||||||
  { provide: ILoggerRepository, useClass: LoggerRepository },
 | 
					  { provide: ILoggerRepository, useClass: LoggerRepository },
 | 
				
			||||||
  { provide: IMachineLearningRepository, useClass: MachineLearningRepository },
 | 
					  { provide: IMachineLearningRepository, useClass: MachineLearningRepository },
 | 
				
			||||||
  { provide: IMapRepository, useClass: MapRepository },
 | 
					  { provide: IMapRepository, useClass: MapRepository },
 | 
				
			||||||
  { provide: IMediaRepository, useClass: MediaRepository },
 | 
					 | 
				
			||||||
  { provide: IMetadataRepository, useClass: MetadataRepository },
 | 
					  { provide: IMetadataRepository, useClass: MetadataRepository },
 | 
				
			||||||
  { provide: IMoveRepository, useClass: MoveRepository },
 | 
					  { provide: IMoveRepository, useClass: MoveRepository },
 | 
				
			||||||
  { provide: INotificationRepository, useClass: NotificationRepository },
 | 
					  { provide: INotificationRepository, useClass: NotificationRepository },
 | 
				
			||||||
 | 
				
			|||||||
@ -12,12 +12,11 @@ import {
 | 
				
			|||||||
  DecodeToBufferOptions,
 | 
					  DecodeToBufferOptions,
 | 
				
			||||||
  GenerateThumbhashOptions,
 | 
					  GenerateThumbhashOptions,
 | 
				
			||||||
  GenerateThumbnailOptions,
 | 
					  GenerateThumbnailOptions,
 | 
				
			||||||
  IMediaRepository,
 | 
					 | 
				
			||||||
  ImageDimensions,
 | 
					  ImageDimensions,
 | 
				
			||||||
  ProbeOptions,
 | 
					  ProbeOptions,
 | 
				
			||||||
  TranscodeCommand,
 | 
					  TranscodeCommand,
 | 
				
			||||||
  VideoInfo,
 | 
					  VideoInfo,
 | 
				
			||||||
} from 'src/interfaces/media.interface';
 | 
					} from 'src/types';
 | 
				
			||||||
import { handlePromiseError } from 'src/utils/misc';
 | 
					import { handlePromiseError } from 'src/utils/misc';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const probe = (input: string, options: string[]): Promise<FfprobeData> =>
 | 
					const probe = (input: string, options: string[]): Promise<FfprobeData> =>
 | 
				
			||||||
@ -37,7 +36,7 @@ type ProgressEvent = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class MediaRepository implements IMediaRepository {
 | 
					export class MediaRepository {
 | 
				
			||||||
  constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) {
 | 
					  constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) {
 | 
				
			||||||
    this.logger.setContext(MediaRepository.name);
 | 
					    this.logger.setContext(MediaRepository.name);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,6 @@ import { ILibraryRepository } from 'src/interfaces/library.interface';
 | 
				
			|||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
					import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
				
			||||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
 | 
					import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
 | 
				
			||||||
import { IMapRepository } from 'src/interfaces/map.interface';
 | 
					import { IMapRepository } from 'src/interfaces/map.interface';
 | 
				
			||||||
import { IMediaRepository } from 'src/interfaces/media.interface';
 | 
					 | 
				
			||||||
import { IMetadataRepository } from 'src/interfaces/metadata.interface';
 | 
					import { IMetadataRepository } from 'src/interfaces/metadata.interface';
 | 
				
			||||||
import { IMoveRepository } from 'src/interfaces/move.interface';
 | 
					import { IMoveRepository } from 'src/interfaces/move.interface';
 | 
				
			||||||
import { INotificationRepository } from 'src/interfaces/notification.interface';
 | 
					import { INotificationRepository } from 'src/interfaces/notification.interface';
 | 
				
			||||||
@ -43,6 +42,7 @@ import { ActivityRepository } from 'src/repositories/activity.repository';
 | 
				
			|||||||
import { ApiKeyRepository } from 'src/repositories/api-key.repository';
 | 
					import { ApiKeyRepository } from 'src/repositories/api-key.repository';
 | 
				
			||||||
import { AuditRepository } from 'src/repositories/audit.repository';
 | 
					import { AuditRepository } from 'src/repositories/audit.repository';
 | 
				
			||||||
import { ConfigRepository } from 'src/repositories/config.repository';
 | 
					import { ConfigRepository } from 'src/repositories/config.repository';
 | 
				
			||||||
 | 
					import { MediaRepository } from 'src/repositories/media.repository';
 | 
				
			||||||
import { MemoryRepository } from 'src/repositories/memory.repository';
 | 
					import { MemoryRepository } from 'src/repositories/memory.repository';
 | 
				
			||||||
import { ViewRepository } from 'src/repositories/view-repository';
 | 
					import { ViewRepository } from 'src/repositories/view-repository';
 | 
				
			||||||
import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
 | 
					import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
 | 
				
			||||||
@ -69,7 +69,7 @@ export class BaseService {
 | 
				
			|||||||
    @Inject(ILibraryRepository) protected libraryRepository: ILibraryRepository,
 | 
					    @Inject(ILibraryRepository) protected libraryRepository: ILibraryRepository,
 | 
				
			||||||
    @Inject(IMachineLearningRepository) protected machineLearningRepository: IMachineLearningRepository,
 | 
					    @Inject(IMachineLearningRepository) protected machineLearningRepository: IMachineLearningRepository,
 | 
				
			||||||
    @Inject(IMapRepository) protected mapRepository: IMapRepository,
 | 
					    @Inject(IMapRepository) protected mapRepository: IMapRepository,
 | 
				
			||||||
    @Inject(IMediaRepository) protected mediaRepository: IMediaRepository,
 | 
					    protected mediaRepository: MediaRepository,
 | 
				
			||||||
    protected memoryRepository: MemoryRepository,
 | 
					    protected memoryRepository: MemoryRepository,
 | 
				
			||||||
    @Inject(IMetadataRepository) protected metadataRepository: IMetadataRepository,
 | 
					    @Inject(IMetadataRepository) protected metadataRepository: IMetadataRepository,
 | 
				
			||||||
    @Inject(IMoveRepository) protected moveRepository: IMoveRepository,
 | 
					    @Inject(IMoveRepository) protected moveRepository: IMoveRepository,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					import { OutputInfo } from 'sharp';
 | 
				
			||||||
import { SystemConfig } from 'src/config';
 | 
					import { SystemConfig } from 'src/config';
 | 
				
			||||||
import { AssetEntity } from 'src/entities/asset.entity';
 | 
					import { AssetEntity } from 'src/entities/asset.entity';
 | 
				
			||||||
import { ExifEntity } from 'src/entities/exif.entity';
 | 
					import { ExifEntity } from 'src/entities/exif.entity';
 | 
				
			||||||
@ -15,12 +16,12 @@ import {
 | 
				
			|||||||
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
 | 
					import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
 | 
				
			||||||
import { IJobRepository, JobCounts, JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
					import { IJobRepository, JobCounts, JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
				
			||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
					import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
				
			||||||
import { IMediaRepository, RawImageInfo } from 'src/interfaces/media.interface';
 | 
					 | 
				
			||||||
import { IMoveRepository } from 'src/interfaces/move.interface';
 | 
					import { IMoveRepository } from 'src/interfaces/move.interface';
 | 
				
			||||||
import { IPersonRepository } from 'src/interfaces/person.interface';
 | 
					import { IPersonRepository } from 'src/interfaces/person.interface';
 | 
				
			||||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
 | 
					import { IStorageRepository } from 'src/interfaces/storage.interface';
 | 
				
			||||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
 | 
					import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
 | 
				
			||||||
import { MediaService } from 'src/services/media.service';
 | 
					import { MediaService } from 'src/services/media.service';
 | 
				
			||||||
 | 
					import { IMediaRepository, RawImageInfo } from 'src/types';
 | 
				
			||||||
import { assetStub } from 'test/fixtures/asset.stub';
 | 
					import { assetStub } from 'test/fixtures/asset.stub';
 | 
				
			||||||
import { faceStub } from 'test/fixtures/face.stub';
 | 
					import { faceStub } from 'test/fixtures/face.stub';
 | 
				
			||||||
import { probeStub } from 'test/fixtures/media.stub';
 | 
					import { probeStub } from 'test/fixtures/media.stub';
 | 
				
			||||||
@ -257,7 +258,7 @@ describe(MediaService.name, () => {
 | 
				
			|||||||
    beforeEach(() => {
 | 
					    beforeEach(() => {
 | 
				
			||||||
      rawBuffer = Buffer.from('image data');
 | 
					      rawBuffer = Buffer.from('image data');
 | 
				
			||||||
      rawInfo = { width: 100, height: 100, channels: 3 };
 | 
					      rawInfo = { width: 100, height: 100, channels: 3 };
 | 
				
			||||||
      mediaMock.decodeImage.mockResolvedValue({ data: rawBuffer, info: rawInfo });
 | 
					      mediaMock.decodeImage.mockResolvedValue({ data: rawBuffer, info: rawInfo as OutputInfo });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should skip thumbnail generation if asset not found', async () => {
 | 
					    it('should skip thumbnail generation if asset not found', async () => {
 | 
				
			||||||
 | 
				
			|||||||
@ -27,8 +27,8 @@ import {
 | 
				
			|||||||
  JobStatus,
 | 
					  JobStatus,
 | 
				
			||||||
  QueueName,
 | 
					  QueueName,
 | 
				
			||||||
} from 'src/interfaces/job.interface';
 | 
					} from 'src/interfaces/job.interface';
 | 
				
			||||||
import { AudioStreamInfo, VideoFormat, VideoInterfaces, VideoStreamInfo } from 'src/interfaces/media.interface';
 | 
					 | 
				
			||||||
import { BaseService } from 'src/services/base.service';
 | 
					import { BaseService } from 'src/services/base.service';
 | 
				
			||||||
 | 
					import { AudioStreamInfo, VideoFormat, VideoInterfaces, VideoStreamInfo } from 'src/types';
 | 
				
			||||||
import { getAssetFiles } from 'src/utils/asset.util';
 | 
					import { getAssetFiles } from 'src/utils/asset.util';
 | 
				
			||||||
import { BaseConfig, ThumbnailConfig } from 'src/utils/media';
 | 
					import { BaseConfig, ThumbnailConfig } from 'src/utils/media';
 | 
				
			||||||
import { mimeTypes } from 'src/utils/mime-types';
 | 
					import { mimeTypes } from 'src/utils/mime-types';
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,6 @@ import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
				
			|||||||
import { IEventRepository } from 'src/interfaces/event.interface';
 | 
					import { IEventRepository } from 'src/interfaces/event.interface';
 | 
				
			||||||
import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
					import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
				
			||||||
import { IMapRepository } from 'src/interfaces/map.interface';
 | 
					import { IMapRepository } from 'src/interfaces/map.interface';
 | 
				
			||||||
import { IMediaRepository } from 'src/interfaces/media.interface';
 | 
					 | 
				
			||||||
import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.interface';
 | 
					import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.interface';
 | 
				
			||||||
import { IPersonRepository } from 'src/interfaces/person.interface';
 | 
					import { IPersonRepository } from 'src/interfaces/person.interface';
 | 
				
			||||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
 | 
					import { IStorageRepository } from 'src/interfaces/storage.interface';
 | 
				
			||||||
@ -19,7 +18,7 @@ import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interf
 | 
				
			|||||||
import { ITagRepository } from 'src/interfaces/tag.interface';
 | 
					import { ITagRepository } from 'src/interfaces/tag.interface';
 | 
				
			||||||
import { IUserRepository } from 'src/interfaces/user.interface';
 | 
					import { IUserRepository } from 'src/interfaces/user.interface';
 | 
				
			||||||
import { MetadataService } from 'src/services/metadata.service';
 | 
					import { MetadataService } from 'src/services/metadata.service';
 | 
				
			||||||
import { IConfigRepository } from 'src/types';
 | 
					import { IConfigRepository, IMediaRepository } from 'src/types';
 | 
				
			||||||
import { assetStub } from 'test/fixtures/asset.stub';
 | 
					import { assetStub } from 'test/fixtures/asset.stub';
 | 
				
			||||||
import { fileStub } from 'test/fixtures/file.stub';
 | 
					import { fileStub } from 'test/fixtures/file.stub';
 | 
				
			||||||
import { probeStub } from 'test/fixtures/media.stub';
 | 
					import { probeStub } from 'test/fixtures/media.stub';
 | 
				
			||||||
 | 
				
			|||||||
@ -7,12 +7,12 @@ import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interfac
 | 
				
			|||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
					import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
				
			||||||
import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
					import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
				
			||||||
import { DetectedFaces, IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
 | 
					import { DetectedFaces, IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
 | 
				
			||||||
import { IMediaRepository } from 'src/interfaces/media.interface';
 | 
					 | 
				
			||||||
import { IPersonRepository } from 'src/interfaces/person.interface';
 | 
					import { IPersonRepository } from 'src/interfaces/person.interface';
 | 
				
			||||||
import { FaceSearchResult, ISearchRepository } from 'src/interfaces/search.interface';
 | 
					import { FaceSearchResult, ISearchRepository } from 'src/interfaces/search.interface';
 | 
				
			||||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
 | 
					import { IStorageRepository } from 'src/interfaces/storage.interface';
 | 
				
			||||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
 | 
					import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
 | 
				
			||||||
import { PersonService } from 'src/services/person.service';
 | 
					import { PersonService } from 'src/services/person.service';
 | 
				
			||||||
 | 
					import { IMediaRepository } from 'src/types';
 | 
				
			||||||
import { ImmichFileResponse } from 'src/utils/file';
 | 
					import { ImmichFileResponse } from 'src/utils/file';
 | 
				
			||||||
import { assetStub } from 'test/fixtures/asset.stub';
 | 
					import { assetStub } from 'test/fixtures/asset.stub';
 | 
				
			||||||
import { authStub } from 'test/fixtures/auth.stub';
 | 
					import { authStub } from 'test/fixtures/auth.stub';
 | 
				
			||||||
 | 
				
			|||||||
@ -42,9 +42,9 @@ import {
 | 
				
			|||||||
  QueueName,
 | 
					  QueueName,
 | 
				
			||||||
} from 'src/interfaces/job.interface';
 | 
					} from 'src/interfaces/job.interface';
 | 
				
			||||||
import { BoundingBox } from 'src/interfaces/machine-learning.interface';
 | 
					import { BoundingBox } from 'src/interfaces/machine-learning.interface';
 | 
				
			||||||
import { CropOptions, ImageDimensions, InputDimensions } from 'src/interfaces/media.interface';
 | 
					 | 
				
			||||||
import { UpdateFacesData } from 'src/interfaces/person.interface';
 | 
					import { UpdateFacesData } from 'src/interfaces/person.interface';
 | 
				
			||||||
import { BaseService } from 'src/services/base.service';
 | 
					import { BaseService } from 'src/services/base.service';
 | 
				
			||||||
 | 
					import { CropOptions, ImageDimensions, InputDimensions } from 'src/types';
 | 
				
			||||||
import { getAssetFiles } from 'src/utils/asset.util';
 | 
					import { getAssetFiles } from 'src/utils/asset.util';
 | 
				
			||||||
import { ImmichFileResponse } from 'src/utils/file';
 | 
					import { ImmichFileResponse } from 'src/utils/file';
 | 
				
			||||||
import { mimeTypes } from 'src/utils/mime-types';
 | 
					import { mimeTypes } from 'src/utils/mime-types';
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,11 @@
 | 
				
			|||||||
import { UserEntity } from 'src/entities/user.entity';
 | 
					import { UserEntity } from 'src/entities/user.entity';
 | 
				
			||||||
import { Permission } from 'src/enum';
 | 
					import { ExifOrientation, ImageFormat, Permission, TranscodeTarget, VideoCodec } from 'src/enum';
 | 
				
			||||||
import { AccessRepository } from 'src/repositories/access.repository';
 | 
					import { AccessRepository } from 'src/repositories/access.repository';
 | 
				
			||||||
import { ActivityRepository } from 'src/repositories/activity.repository';
 | 
					import { ActivityRepository } from 'src/repositories/activity.repository';
 | 
				
			||||||
import { ApiKeyRepository } from 'src/repositories/api-key.repository';
 | 
					import { ApiKeyRepository } from 'src/repositories/api-key.repository';
 | 
				
			||||||
import { AuditRepository } from 'src/repositories/audit.repository';
 | 
					import { AuditRepository } from 'src/repositories/audit.repository';
 | 
				
			||||||
import { ConfigRepository } from 'src/repositories/config.repository';
 | 
					import { ConfigRepository } from 'src/repositories/config.repository';
 | 
				
			||||||
 | 
					import { MediaRepository } from 'src/repositories/media.repository';
 | 
				
			||||||
import { MemoryRepository } from 'src/repositories/memory.repository';
 | 
					import { MemoryRepository } from 'src/repositories/memory.repository';
 | 
				
			||||||
import { ViewRepository } from 'src/repositories/view-repository';
 | 
					import { ViewRepository } from 'src/repositories/view-repository';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -24,6 +25,7 @@ export type IAccessRepository = { [K in keyof AccessRepository]: RepositoryInter
 | 
				
			|||||||
export type IApiKeyRepository = RepositoryInterface<ApiKeyRepository>;
 | 
					export type IApiKeyRepository = RepositoryInterface<ApiKeyRepository>;
 | 
				
			||||||
export type IAuditRepository = RepositoryInterface<AuditRepository>;
 | 
					export type IAuditRepository = RepositoryInterface<AuditRepository>;
 | 
				
			||||||
export type IConfigRepository = RepositoryInterface<ConfigRepository>;
 | 
					export type IConfigRepository = RepositoryInterface<ConfigRepository>;
 | 
				
			||||||
 | 
					export type IMediaRepository = RepositoryInterface<MediaRepository>;
 | 
				
			||||||
export type IMemoryRepository = RepositoryInterface<MemoryRepository>;
 | 
					export type IMemoryRepository = RepositoryInterface<MemoryRepository>;
 | 
				
			||||||
export type IViewRepository = RepositoryInterface<ViewRepository>;
 | 
					export type IViewRepository = RepositoryInterface<ViewRepository>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -39,3 +41,135 @@ export type ApiKeyItem =
 | 
				
			|||||||
export type MemoryItem =
 | 
					export type MemoryItem =
 | 
				
			||||||
  | Awaited<ReturnType<IMemoryRepository['create']>>
 | 
					  | Awaited<ReturnType<IMemoryRepository['create']>>
 | 
				
			||||||
  | Awaited<ReturnType<IMemoryRepository['search']>>[0];
 | 
					  | Awaited<ReturnType<IMemoryRepository['search']>>[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface CropOptions {
 | 
				
			||||||
 | 
					  top: number;
 | 
				
			||||||
 | 
					  left: number;
 | 
				
			||||||
 | 
					  width: number;
 | 
				
			||||||
 | 
					  height: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ImageOptions {
 | 
				
			||||||
 | 
					  format: ImageFormat;
 | 
				
			||||||
 | 
					  quality: number;
 | 
				
			||||||
 | 
					  size: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface RawImageInfo {
 | 
				
			||||||
 | 
					  width: number;
 | 
				
			||||||
 | 
					  height: number;
 | 
				
			||||||
 | 
					  channels: 1 | 2 | 3 | 4;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface DecodeImageOptions {
 | 
				
			||||||
 | 
					  colorspace: string;
 | 
				
			||||||
 | 
					  crop?: CropOptions;
 | 
				
			||||||
 | 
					  processInvalidImages: boolean;
 | 
				
			||||||
 | 
					  raw?: RawImageInfo;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface DecodeToBufferOptions extends DecodeImageOptions {
 | 
				
			||||||
 | 
					  size: number;
 | 
				
			||||||
 | 
					  orientation?: ExifOrientation;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type GenerateThumbnailOptions = ImageOptions & DecodeImageOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type GenerateThumbnailFromBufferOptions = GenerateThumbnailOptions & { raw: RawImageInfo };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type GenerateThumbhashOptions = DecodeImageOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type GenerateThumbhashFromBufferOptions = GenerateThumbhashOptions & { raw: RawImageInfo };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface GenerateThumbnailsOptions {
 | 
				
			||||||
 | 
					  colorspace: string;
 | 
				
			||||||
 | 
					  crop?: CropOptions;
 | 
				
			||||||
 | 
					  preview?: ImageOptions;
 | 
				
			||||||
 | 
					  processInvalidImages: boolean;
 | 
				
			||||||
 | 
					  thumbhash?: boolean;
 | 
				
			||||||
 | 
					  thumbnail?: ImageOptions;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface VideoStreamInfo {
 | 
				
			||||||
 | 
					  index: number;
 | 
				
			||||||
 | 
					  height: number;
 | 
				
			||||||
 | 
					  width: number;
 | 
				
			||||||
 | 
					  rotation: number;
 | 
				
			||||||
 | 
					  codecName?: string;
 | 
				
			||||||
 | 
					  frameCount: number;
 | 
				
			||||||
 | 
					  isHDR: boolean;
 | 
				
			||||||
 | 
					  bitrate: number;
 | 
				
			||||||
 | 
					  pixelFormat: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface AudioStreamInfo {
 | 
				
			||||||
 | 
					  index: number;
 | 
				
			||||||
 | 
					  codecName?: string;
 | 
				
			||||||
 | 
					  frameCount: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface VideoFormat {
 | 
				
			||||||
 | 
					  formatName?: string;
 | 
				
			||||||
 | 
					  formatLongName?: string;
 | 
				
			||||||
 | 
					  duration: number;
 | 
				
			||||||
 | 
					  bitrate: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ImageDimensions {
 | 
				
			||||||
 | 
					  width: number;
 | 
				
			||||||
 | 
					  height: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface InputDimensions extends ImageDimensions {
 | 
				
			||||||
 | 
					  inputPath: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface VideoInfo {
 | 
				
			||||||
 | 
					  format: VideoFormat;
 | 
				
			||||||
 | 
					  videoStreams: VideoStreamInfo[];
 | 
				
			||||||
 | 
					  audioStreams: AudioStreamInfo[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface TranscodeCommand {
 | 
				
			||||||
 | 
					  inputOptions: string[];
 | 
				
			||||||
 | 
					  outputOptions: string[];
 | 
				
			||||||
 | 
					  twoPass: boolean;
 | 
				
			||||||
 | 
					  progress: {
 | 
				
			||||||
 | 
					    frameCount: number;
 | 
				
			||||||
 | 
					    percentInterval: number;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface BitrateDistribution {
 | 
				
			||||||
 | 
					  max: number;
 | 
				
			||||||
 | 
					  target: number;
 | 
				
			||||||
 | 
					  min: number;
 | 
				
			||||||
 | 
					  unit: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ImageBuffer {
 | 
				
			||||||
 | 
					  data: Buffer;
 | 
				
			||||||
 | 
					  info: RawImageInfo;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface VideoCodecSWConfig {
 | 
				
			||||||
 | 
					  getCommand(
 | 
				
			||||||
 | 
					    target: TranscodeTarget,
 | 
				
			||||||
 | 
					    videoStream: VideoStreamInfo,
 | 
				
			||||||
 | 
					    audioStream: AudioStreamInfo,
 | 
				
			||||||
 | 
					    format?: VideoFormat,
 | 
				
			||||||
 | 
					  ): TranscodeCommand;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface VideoCodecHWConfig extends VideoCodecSWConfig {
 | 
				
			||||||
 | 
					  getSupportedCodecs(): Array<VideoCodec>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ProbeOptions {
 | 
				
			||||||
 | 
					  countFrames: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface VideoInterfaces {
 | 
				
			||||||
 | 
					  dri: string[];
 | 
				
			||||||
 | 
					  mali: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import {
 | 
				
			|||||||
  VideoFormat,
 | 
					  VideoFormat,
 | 
				
			||||||
  VideoInterfaces,
 | 
					  VideoInterfaces,
 | 
				
			||||||
  VideoStreamInfo,
 | 
					  VideoStreamInfo,
 | 
				
			||||||
} from 'src/interfaces/media.interface';
 | 
					} from 'src/types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class BaseConfig implements VideoCodecSWConfig {
 | 
					export class BaseConfig implements VideoCodecSWConfig {
 | 
				
			||||||
  readonly presets = ['veryslow', 'slower', 'slow', 'medium', 'fast', 'faster', 'veryfast', 'superfast', 'ultrafast'];
 | 
					  readonly presets = ['veryslow', 'slower', 'slow', 'medium', 'fast', 'faster', 'veryfast', 'superfast', 'ultrafast'];
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								server/test/fixtures/media.stub.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								server/test/fixtures/media.stub.ts
									
									
									
									
										vendored
									
									
								
							@ -1,4 +1,4 @@
 | 
				
			|||||||
import { AudioStreamInfo, VideoFormat, VideoInfo, VideoStreamInfo } from 'src/interfaces/media.interface';
 | 
					import { AudioStreamInfo, VideoFormat, VideoInfo, VideoStreamInfo } from 'src/types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const probeStubDefaultFormat: VideoFormat = {
 | 
					const probeStubDefaultFormat: VideoFormat = {
 | 
				
			||||||
  formatName: 'mov,mp4,m4a,3gp,3g2,mj2',
 | 
					  formatName: 'mov,mp4,m4a,3gp,3g2,mj2',
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { IMediaRepository } from 'src/interfaces/media.interface';
 | 
					import { IMediaRepository } from 'src/types';
 | 
				
			||||||
import { Mocked, vitest } from 'vitest';
 | 
					import { Mocked, vitest } from 'vitest';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const newMediaRepositoryMock = (): Mocked<IMediaRepository> => {
 | 
					export const newMediaRepositoryMock = (): Mocked<IMediaRepository> => {
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,7 @@ import { AccessRepository } from 'src/repositories/access.repository';
 | 
				
			|||||||
import { ActivityRepository } from 'src/repositories/activity.repository';
 | 
					import { ActivityRepository } from 'src/repositories/activity.repository';
 | 
				
			||||||
import { ApiKeyRepository } from 'src/repositories/api-key.repository';
 | 
					import { ApiKeyRepository } from 'src/repositories/api-key.repository';
 | 
				
			||||||
import { AuditRepository } from 'src/repositories/audit.repository';
 | 
					import { AuditRepository } from 'src/repositories/audit.repository';
 | 
				
			||||||
 | 
					import { MediaRepository } from 'src/repositories/media.repository';
 | 
				
			||||||
import { MemoryRepository } from 'src/repositories/memory.repository';
 | 
					import { MemoryRepository } from 'src/repositories/memory.repository';
 | 
				
			||||||
import { ViewRepository } from 'src/repositories/view-repository';
 | 
					import { ViewRepository } from 'src/repositories/view-repository';
 | 
				
			||||||
import { BaseService } from 'src/services/base.service';
 | 
					import { BaseService } from 'src/services/base.service';
 | 
				
			||||||
@ -15,6 +16,7 @@ import {
 | 
				
			|||||||
  IActivityRepository,
 | 
					  IActivityRepository,
 | 
				
			||||||
  IApiKeyRepository,
 | 
					  IApiKeyRepository,
 | 
				
			||||||
  IAuditRepository,
 | 
					  IAuditRepository,
 | 
				
			||||||
 | 
					  IMediaRepository,
 | 
				
			||||||
  IMemoryRepository,
 | 
					  IMemoryRepository,
 | 
				
			||||||
  IViewRepository,
 | 
					  IViewRepository,
 | 
				
			||||||
} from 'src/types';
 | 
					} from 'src/types';
 | 
				
			||||||
@ -133,7 +135,7 @@ export const newTestService = <T extends BaseService>(
 | 
				
			|||||||
    libraryMock,
 | 
					    libraryMock,
 | 
				
			||||||
    machineLearningMock,
 | 
					    machineLearningMock,
 | 
				
			||||||
    mapMock,
 | 
					    mapMock,
 | 
				
			||||||
    mediaMock,
 | 
					    mediaMock as IMediaRepository as MediaRepository,
 | 
				
			||||||
    memoryMock as IMemoryRepository as MemoryRepository,
 | 
					    memoryMock as IMemoryRepository as MemoryRepository,
 | 
				
			||||||
    metadataMock,
 | 
					    metadataMock,
 | 
				
			||||||
    moveMock,
 | 
					    moveMock,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user