merge: remote-tracking branch 'origin/main' into feat/database-restores

This commit is contained in:
izzy
2025-11-28 12:04:43 +00:00
53 changed files with 2349 additions and 2137 deletions

View File

@@ -50,6 +50,7 @@ export class AssetDeltaSyncResponseDto {
export const extraSyncModels: Function[] = [];
export const ExtraModel = (): ClassDecorator => {
// eslint-disable-next-line unicorn/consistent-function-scoping
return (object: Function) => {
extraSyncModels.push(object);
};

View File

@@ -257,7 +257,7 @@ describe('getEnv', () => {
expect(telemetry).toEqual({
apiPort: 8081,
microservicesPort: 8082,
metrics: new Set([]),
metrics: new Set(),
});
});

View File

@@ -669,7 +669,7 @@ describe(AlbumService.name, () => {
});
it('should not allow a shared user with viewer access to add assets', async () => {
mocks.access.album.checkSharedAlbumAccess.mockResolvedValue(new Set([]));
mocks.access.album.checkSharedAlbumAccess.mockResolvedValue(new Set());
mocks.album.getById.mockResolvedValue(_.cloneDeep(albumStub.sharedWithUser));
await expect(

View File

@@ -278,7 +278,7 @@ describe(LibraryService.name, () => {
mocks.library.get.mockResolvedValue(library);
mocks.storage.walk.mockImplementation(async function* generator() {});
mocks.asset.getLibraryAssetCount.mockResolvedValue(1);
mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: BigInt(1) });
mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: 1n });
const response = await sut.handleQueueSyncAssets({ id: library.id });
@@ -296,7 +296,7 @@ describe(LibraryService.name, () => {
mocks.library.get.mockResolvedValue(library);
mocks.storage.walk.mockImplementation(async function* generator() {});
mocks.asset.getLibraryAssetCount.mockResolvedValue(0);
mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: BigInt(1) });
mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: 1n });
const response = await sut.handleQueueSyncAssets({ id: library.id });
@@ -311,7 +311,7 @@ describe(LibraryService.name, () => {
mocks.storage.walk.mockImplementation(async function* generator() {});
mocks.library.streamAssetIds.mockReturnValue(makeStream([assetStub.external]));
mocks.asset.getLibraryAssetCount.mockResolvedValue(1);
mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: BigInt(0) });
mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: 0n });
mocks.library.streamAssetIds.mockReturnValue(makeStream([assetStub.external]));
const response = await sut.handleQueueSyncAssets({ id: library.id });

View File

@@ -223,7 +223,14 @@ export class LibraryService extends BaseService {
ownerId: dto.ownerId,
name: dto.name ?? 'New External Library',
importPaths: dto.importPaths ?? [],
exclusionPatterns: dto.exclusionPatterns ?? ['**/@eaDir/**', '**/._*', '**/#recycle/**', '**/#snapshot/**'],
exclusionPatterns: dto.exclusionPatterns ?? [
'**/@eaDir/**',
'**/._*',
'**/#recycle/**',
'**/#snapshot/**',
'**/.stversions/**',
'**/.stfolder/**',
],
});
return mapLibrary(library);
}

View File

@@ -561,7 +561,7 @@ export class MediaService extends BaseService {
private getMainStream<T extends VideoStreamInfo | AudioStreamInfo>(streams: T[]): T {
return streams
.filter((stream) => stream.codecName !== 'unknown')
.sort((stream1, stream2) => stream2.bitrate - stream1.bitrate)[0];
.toSorted((stream1, stream2) => stream2.bitrate - stream1.bitrate)[0];
}
private getTranscodeTarget(

View File

@@ -37,7 +37,6 @@ import { upsertTags } from 'src/utils/tag';
const EXIF_DATE_TAGS: Array<keyof ImmichTags> = [
'SubSecDateTimeOriginal',
'SubSecCreateDate',
'SubSecMediaCreateDate',
'DateTimeOriginal',
'CreationDate',
'CreateDate',

View File

@@ -217,7 +217,7 @@ describe(TagService.name, () => {
describe('addAssets', () => {
it('should handle invalid ids', async () => {
mocks.tag.getAssetIds.mockResolvedValue(new Set([]));
mocks.tag.getAssetIds.mockResolvedValue(new Set());
await expect(sut.addAssets(authStub.admin, 'tag-1', { ids: ['asset-1'] })).resolves.toEqual([
{ id: 'asset-1', success: false, error: 'no_permission' },
]);

View File

@@ -46,7 +46,7 @@ export const setIsEqual = (source: Set<unknown>, target: Set<unknown>) =>
source.size === target.size && [...source].every((x) => target.has(x));
export const haveEqualColumns = (sourceColumns?: string[], targetColumns?: string[]) => {
return setIsEqual(new Set(sourceColumns ?? []), new Set(targetColumns ?? []));
return setIsEqual(new Set(sourceColumns), new Set(targetColumns));
};
export const haveEqualOverrides = <T extends { override?: DatabaseOverride }>(source: T, target: T) => {

View File

@@ -704,8 +704,7 @@ export class QsvSwDecodeConfig extends BaseHWConfig {
}
getBitrateOptions() {
const options = [];
options.push(`-${this.useCQP() ? 'q:v' : 'global_quality:v'} ${this.config.crf}`);
const options = [`-${this.useCQP() ? 'q:v' : 'global_quality:v'} ${this.config.crf}`];
const bitrates = this.getBitrateDistribution();
if (bitrates.max > 0) {
options.push(`-maxrate ${bitrates.max}${bitrates.unit}`, `-bufsize ${bitrates.max * 2}${bitrates.unit}`);

View File

@@ -139,7 +139,7 @@ function sortKeys<T>(target: T): T {
}
const result: Partial<T> = {};
const keys = Object.keys(target).sort() as Array<keyof T>;
const keys = Object.keys(target).toSorted() as Array<keyof T>;
for (const key of keys) {
result[key] = sortKeys(target[key]);
}
@@ -178,10 +178,7 @@ const patchOpenAPI = (document: OpenAPIObject) => {
throw new Error(`Invalid number format: ${schemaName}.${key}=float (use double instead). `);
}
}
if (schema.required) {
schema.required = schema.required.sort();
}
schema.required?.sort();
}
}
}