mirror of
https://github.com/immich-app/immich.git
synced 2025-02-03 01:22:44 +01:00
Compare commits
7 commits
b579172309
...
85282a7328
Author | SHA1 | Date | |
---|---|---|---|
|
85282a7328 | ||
|
060300de8a | ||
|
c2ba1cc202 | ||
|
08db77db23 | ||
|
92dff839d0 | ||
|
fe1e09e51f | ||
|
f44669447f |
30 changed files with 140 additions and 44 deletions
cli
docs
e2e
machine-learning
mobile
open-api
server
web
package-lock.jsonpackage.json
src
lib
components/asset-viewer
utils
routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]
6
cli/package-lock.json
generated
6
cli/package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@immich/cli",
|
||||
"version": "2.2.46",
|
||||
"version": "2.2.47",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@immich/cli",
|
||||
"version": "2.2.46",
|
||||
"version": "2.2.47",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
"fast-glob": "^3.3.2",
|
||||
|
@ -52,7 +52,7 @@
|
|||
},
|
||||
"../open-api/typescript-sdk": {
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"dev": true,
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@immich/cli",
|
||||
"version": "2.2.46",
|
||||
"version": "2.2.47",
|
||||
"description": "Command Line Interface (CLI) for Immich",
|
||||
"type": "module",
|
||||
"exports": "./dist/index.js",
|
||||
|
|
|
@ -99,6 +99,11 @@ const projects: CommunityProjectProps[] = [
|
|||
description: 'Downloads a configurable number of random photos based on people or album ID.',
|
||||
url: 'https://github.com/jon6fingrs/immich-dl',
|
||||
},
|
||||
{
|
||||
title: 'Immich Upload Optimizer',
|
||||
description: 'Automatically optimize files uploaded to Immich in order to save storage space',
|
||||
url: 'https://github.com/miguelangel-nubla/immich-upload-optimizer',
|
||||
},
|
||||
];
|
||||
|
||||
function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element {
|
||||
|
|
4
docs/static/archived-versions.json
vendored
4
docs/static/archived-versions.json
vendored
|
@ -1,4 +1,8 @@
|
|||
[
|
||||
{
|
||||
"label": "v1.125.6",
|
||||
"url": "https://v1.125.6.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.125.5",
|
||||
"url": "https://v1.125.5.archive.immich.app"
|
||||
|
|
8
e2e/package-lock.json
generated
8
e2e/package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "immich-e2e",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "immich-e2e",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
|
@ -45,7 +45,7 @@
|
|||
},
|
||||
"../cli": {
|
||||
"name": "@immich/cli",
|
||||
"version": "2.2.46",
|
||||
"version": "2.2.47",
|
||||
"dev": true,
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
|
@ -92,7 +92,7 @@
|
|||
},
|
||||
"../open-api/typescript-sdk": {
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"dev": true,
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "immich-e2e",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
|
|
|
@ -701,6 +701,20 @@ describe('/asset', () => {
|
|||
expect(status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should set the negative rating', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/assets/${user1Assets[0].id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({ rating: -1 });
|
||||
expect(body).toMatchObject({
|
||||
id: user1Assets[0].id,
|
||||
exifInfo: expect.objectContaining({
|
||||
rating: -1,
|
||||
}),
|
||||
});
|
||||
expect(status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should reject invalid rating', async () => {
|
||||
for (const test of [{ rating: 7 }, { rating: 3.5 }, { rating: null }]) {
|
||||
const { status, body } = await request(app)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "machine-learning"
|
||||
version = "1.125.5"
|
||||
version = "1.125.6"
|
||||
description = ""
|
||||
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
||||
readme = "README.md"
|
||||
|
|
|
@ -35,8 +35,8 @@ platform :android do
|
|||
task: 'bundle',
|
||||
build_type: 'Release',
|
||||
properties: {
|
||||
"android.injected.version.code" => 181,
|
||||
"android.injected.version.name" => "1.125.5",
|
||||
"android.injected.version.code" => 182,
|
||||
"android.injected.version.name" => "1.125.6",
|
||||
}
|
||||
)
|
||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||
|
|
|
@ -19,7 +19,7 @@ platform :ios do
|
|||
desc "iOS Release"
|
||||
lane :release do
|
||||
increment_version_number(
|
||||
version_number: "1.125.5"
|
||||
version_number: "1.125.6"
|
||||
)
|
||||
increment_build_number(
|
||||
build_number: latest_testflight_build_number + 1,
|
||||
|
|
BIN
mobile/openapi/README.md
generated
BIN
mobile/openapi/README.md
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/asset_bulk_update_dto.dart
generated
BIN
mobile/openapi/lib/model/asset_bulk_update_dto.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/update_asset_dto.dart
generated
BIN
mobile/openapi/lib/model/update_asset_dto.dart
generated
Binary file not shown.
|
@ -2,7 +2,7 @@ name: immich_mobile
|
|||
description: Immich - selfhosted backup media file on mobile phone
|
||||
|
||||
publish_to: 'none'
|
||||
version: 1.125.5+181
|
||||
version: 1.125.6+182
|
||||
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
|
|
|
@ -7454,7 +7454,7 @@
|
|||
"info": {
|
||||
"title": "Immich",
|
||||
"description": "Immich API",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"contact": {}
|
||||
},
|
||||
"tags": [],
|
||||
|
@ -7951,7 +7951,7 @@
|
|||
},
|
||||
"rating": {
|
||||
"maximum": 5,
|
||||
"minimum": 0,
|
||||
"minimum": -1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
|
@ -12780,7 +12780,7 @@
|
|||
},
|
||||
"rating": {
|
||||
"maximum": 5,
|
||||
"minimum": 0,
|
||||
"minimum": -1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
|
|
4
open-api/typescript-sdk/package-lock.json
generated
4
open-api/typescript-sdk/package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
"@oazapfts/runtime": "^1.0.2"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"description": "Auto-generated TypeScript SDK for the Immich API",
|
||||
"type": "module",
|
||||
"main": "./build/index.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Immich
|
||||
* 1.125.5
|
||||
* 1.125.6
|
||||
* DO NOT MODIFY - This file has been generated using oazapfts.
|
||||
* See https://www.npmjs.com/package/oazapfts
|
||||
*/
|
||||
|
|
4
server/package-lock.json
generated
4
server/package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "immich",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "immich",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
"@nestjs/bullmq": "^11.0.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "immich",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
|
|
|
@ -52,7 +52,7 @@ export class UpdateAssetBase {
|
|||
@Optional()
|
||||
@IsInt()
|
||||
@Max(5)
|
||||
@Min(0)
|
||||
@Min(-1)
|
||||
rating?: number;
|
||||
}
|
||||
|
||||
|
|
|
@ -1162,6 +1162,17 @@ describe(MetadataService.name, () => {
|
|||
}),
|
||||
);
|
||||
});
|
||||
it('should handle valid negative rating value', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
mockReadTags({ Rating: -1 });
|
||||
|
||||
await sut.handleMetadataExtraction({ id: assetStub.image.id });
|
||||
expect(assetMock.upsertExif).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
rating: -1,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleQueueSidecar', () => {
|
||||
|
|
|
@ -213,7 +213,7 @@ export class MetadataService extends BaseService {
|
|||
// comments
|
||||
description: String(exifTags.ImageDescription || exifTags.Description || '').trim(),
|
||||
profileDescription: exifTags.ProfileDescription || null,
|
||||
rating: validateRange(exifTags.Rating, 0, 5),
|
||||
rating: validateRange(exifTags.Rating, -1, 5),
|
||||
|
||||
// grouping
|
||||
livePhotoCID: (exifTags.ContentIdentifier || exifTags.MediaGroupUUID) ?? null,
|
||||
|
|
27
web/package-lock.json
generated
27
web/package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "immich-web",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "immich-web",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
||||
|
@ -16,6 +16,8 @@
|
|||
"@mdi/js": "^7.4.47",
|
||||
"@photo-sphere-viewer/core": "^5.11.5",
|
||||
"@photo-sphere-viewer/equirectangular-video-adapter": "^5.11.5",
|
||||
"@photo-sphere-viewer/resolution-plugin": "^5.11.5",
|
||||
"@photo-sphere-viewer/settings-plugin": "^5.11.5",
|
||||
"@photo-sphere-viewer/video-plugin": "^5.11.5",
|
||||
"@zoom-image/svelte": "^0.3.0",
|
||||
"dom-to-image": "^2.6.0",
|
||||
|
@ -75,7 +77,7 @@
|
|||
},
|
||||
"../open-api/typescript-sdk": {
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
"@oazapfts/runtime": "^1.0.2"
|
||||
|
@ -1669,6 +1671,25 @@
|
|||
"@photo-sphere-viewer/video-plugin": "5.11.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@photo-sphere-viewer/resolution-plugin": {
|
||||
"version": "5.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.11.5.tgz",
|
||||
"integrity": "sha512-Dbvp5bBtozD3IWt1Q0wORVaZBcB1bV9xUeoOS9A7F7b3EkQ2pkC5/jot/1AyM4wtU5wJ63NWHskQ1d7m6WWazQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@photo-sphere-viewer/core": "5.11.5",
|
||||
"@photo-sphere-viewer/settings-plugin": "5.11.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@photo-sphere-viewer/settings-plugin": {
|
||||
"version": "5.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.11.5.tgz",
|
||||
"integrity": "sha512-ZgYaWjiBMhsoRH5ddW3h+v4J4LPmofsT7BBRq5UCssWw2Fsrvv7mFFRi4UbZ1qzeKmvNUOr8BaFQgX1ZLvUWfQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@photo-sphere-viewer/core": "5.11.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@photo-sphere-viewer/video-plugin": {
|
||||
"version": "5.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.11.5.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "immich-web",
|
||||
"version": "1.125.5",
|
||||
"version": "1.125.6",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"scripts": {
|
||||
"dev": "vite dev --host 0.0.0.0 --port 3000",
|
||||
|
@ -72,6 +72,8 @@
|
|||
"@mdi/js": "^7.4.47",
|
||||
"@photo-sphere-viewer/core": "^5.11.5",
|
||||
"@photo-sphere-viewer/equirectangular-video-adapter": "^5.11.5",
|
||||
"@photo-sphere-viewer/resolution-plugin": "^5.11.5",
|
||||
"@photo-sphere-viewer/settings-plugin": "^5.11.5",
|
||||
"@photo-sphere-viewer/video-plugin": "^5.11.5",
|
||||
"@zoom-image/svelte": "^0.3.0",
|
||||
"dom-to-image": "^2.6.0",
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
{:then [data, { default: PhotoSphereViewer }]}
|
||||
<PhotoSphereViewer
|
||||
panorama={data}
|
||||
originalImageUrl={isWebCompatibleImage(asset) ? getAssetOriginalUrl(asset.id) : undefined}
|
||||
originalPanorama={isWebCompatibleImage(asset) ? getAssetOriginalUrl(asset.id) : undefined}
|
||||
/>
|
||||
{:catch}
|
||||
{$t('errors.failed_to_load_asset')}
|
||||
|
|
|
@ -7,18 +7,21 @@
|
|||
type AdapterConstructor,
|
||||
type PluginConstructor,
|
||||
} from '@photo-sphere-viewer/core';
|
||||
import { SettingsPlugin } from '@photo-sphere-viewer/settings-plugin';
|
||||
import { ResolutionPlugin } from '@photo-sphere-viewer/resolution-plugin';
|
||||
import '@photo-sphere-viewer/core/index.css';
|
||||
import '@photo-sphere-viewer/settings-plugin/index.css';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
panorama: string | { source: string };
|
||||
originalImageUrl?: string;
|
||||
originalPanorama?: string | { source: string };
|
||||
adapter?: AdapterConstructor | [AdapterConstructor, unknown];
|
||||
plugins?: (PluginConstructor | [PluginConstructor, unknown])[];
|
||||
navbar?: boolean;
|
||||
}
|
||||
|
||||
let { panorama, originalImageUrl, adapter = EquirectangularAdapter, plugins = [], navbar = false }: Props = $props();
|
||||
let { panorama, originalPanorama, adapter = EquirectangularAdapter, plugins = [], navbar = false }: Props = $props();
|
||||
|
||||
let container: HTMLDivElement | undefined = $state();
|
||||
let viewer: Viewer;
|
||||
|
@ -30,9 +33,33 @@
|
|||
|
||||
viewer = new Viewer({
|
||||
adapter,
|
||||
plugins,
|
||||
plugins: [
|
||||
SettingsPlugin,
|
||||
[
|
||||
ResolutionPlugin,
|
||||
{
|
||||
defaultResolution: $alwaysLoadOriginalFile && originalPanorama ? 'original' : 'default',
|
||||
resolutions: [
|
||||
{
|
||||
id: 'default',
|
||||
label: 'Default',
|
||||
panorama,
|
||||
},
|
||||
...(originalPanorama
|
||||
? [
|
||||
{
|
||||
id: 'original',
|
||||
label: 'Original',
|
||||
panorama: originalPanorama,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
],
|
||||
...plugins,
|
||||
],
|
||||
container,
|
||||
panorama,
|
||||
touchmoveTwoFingers: false,
|
||||
mousewheelCtrlKey: false,
|
||||
navbar,
|
||||
|
@ -40,15 +67,14 @@
|
|||
maxFov: 120,
|
||||
fisheye: false,
|
||||
});
|
||||
const resolutionPlugin = viewer.getPlugin(ResolutionPlugin) as ResolutionPlugin;
|
||||
|
||||
if (originalImageUrl && !$alwaysLoadOriginalFile) {
|
||||
if (originalPanorama && !$alwaysLoadOriginalFile) {
|
||||
const zoomHandler = ({ zoomLevel }: events.ZoomUpdatedEvent) => {
|
||||
// zoomLevel range: [0, 100]
|
||||
if (Math.round(zoomLevel) >= 75) {
|
||||
// Replace the preview with the original
|
||||
viewer.setPanorama(originalImageUrl, { showLoader: false, speed: 150 }).catch(() => {
|
||||
viewer.setPanorama(panorama, { showLoader: false, speed: 0 }).catch(() => {});
|
||||
});
|
||||
void resolutionPlugin.setResolution('original');
|
||||
viewer.removeEventListener(events.ZoomUpdatedEvent.type, zoomHandler);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getAssetOriginalUrl } from '$lib/utils';
|
||||
import { getAssetPlaybackUrl, getAssetOriginalUrl } from '$lib/utils';
|
||||
import { fade } from 'svelte/transition';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
@ -22,7 +22,13 @@
|
|||
{#await modules}
|
||||
<LoadingSpinner />
|
||||
{:then [PhotoSphereViewer, adapter, videoPlugin]}
|
||||
<PhotoSphereViewer panorama={{ source: getAssetOriginalUrl(assetId) }} plugins={[videoPlugin]} {adapter} navbar />
|
||||
<PhotoSphereViewer
|
||||
panorama={{ source: getAssetPlaybackUrl(assetId) }}
|
||||
originalPanorama={{ source: getAssetOriginalUrl(assetId) }}
|
||||
plugins={[videoPlugin]}
|
||||
{adapter}
|
||||
navbar
|
||||
/>
|
||||
{:catch}
|
||||
{$t('errors.failed_to_load_asset')}
|
||||
{/await}
|
||||
|
|
|
@ -157,7 +157,6 @@ async function fileUploader(
|
|||
}
|
||||
} catch (error) {
|
||||
console.error(`Error calculating sha1 file=${assetFile.name})`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@
|
|||
let personMerge1: PersonResponseDto | undefined = $state();
|
||||
let personMerge2: PersonResponseDto | undefined = $state();
|
||||
let potentialMergePeople: PersonResponseDto[] = $state([]);
|
||||
let isSuggestionSelectedByUser = $state(false);
|
||||
|
||||
let personName = '';
|
||||
let suggestedPeople: PersonResponseDto[] = $state([]);
|
||||
|
@ -233,15 +234,22 @@
|
|||
personName = person.name;
|
||||
personMerge1 = person;
|
||||
personMerge2 = person2;
|
||||
isSuggestionSelectedByUser = true;
|
||||
viewMode = PersonPageViewMode.SUGGEST_MERGE;
|
||||
};
|
||||
|
||||
const changeName = async () => {
|
||||
viewMode = PersonPageViewMode.VIEW_ASSETS;
|
||||
person.name = personName;
|
||||
try {
|
||||
isEditingName = false;
|
||||
isEditingName = false;
|
||||
|
||||
if (isSuggestionSelectedByUser) {
|
||||
// User canceled the merge
|
||||
isSuggestionSelectedByUser = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
person = await updatePerson({ id: person.id, personUpdateDto: { name: personName } });
|
||||
|
||||
notificationController.show({
|
||||
|
|
Loading…
Reference in a new issue