1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-02-03 01:22:44 +01:00

Compare commits

...

7 commits

Author SHA1 Message Date
Jonathan Jogenfors
85282a7328
Merge b632b84fc3 into 060300de8a 2025-01-28 12:46:17 -05:00
André Ventura
060300de8a
fix(web): cancel people merge selection: do not show "Change name successfully" notification ()
fix(web): cancel people merge selection: do not show "Change name successfully" notification.

Co-authored-by: André Ventura <afv@users.noreply.github.com>
2025-01-28 11:43:52 -06:00
Miguel Angel Nubla
c2ba1cc202
docs: add immich-upload-optimizer to Community Projects list () 2025-01-28 09:40:00 -06:00
PastLeo
08db77db23
feat: resolution selection and default preview playback for 360° panorama videos ()
* original/preview switching in photo-sphere-viewer

1. default to preview in photo-sphere-viewer video mode
2. install and integrate @photo-sphere-viewer/settings-plugin & @photo-sphere-viewer/resolution-plugin

* fix lint errors
2025-01-28 09:09:40 -06:00
RiggiG
92dff839d0
fix(web): do not throw error when hash fails ()
change: do not throw error when hash fails
2025-01-28 03:54:56 +00:00
Christian Kündig
fe1e09e51f
fix(server): Allow negative rating (for rejected images) ()
Allow negative rating (for rejected images)
2025-01-27 21:54:29 -06:00
github-actions
f44669447f chore: version v1.125.6 2025-01-28 02:58:27 +00:00
30 changed files with 140 additions and 44 deletions

6
cli/package-lock.json generated
View file

@ -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": {

View file

@ -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",

View file

@ -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 {

View file

@ -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
View file

@ -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": {

View file

@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "1.125.5",
"version": "1.125.6",
"description": "",
"main": "index.js",
"type": "module",

View file

@ -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)

View file

@ -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"

View file

@ -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')

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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'

View file

@ -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"
}
},

View file

@ -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"

View file

@ -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",

View file

@ -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
*/

View file

@ -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",

View file

@ -1,6 +1,6 @@
{
"name": "immich",
"version": "1.125.5",
"version": "1.125.6",
"description": "",
"author": "",
"private": true,

View file

@ -52,7 +52,7 @@ export class UpdateAssetBase {
@Optional()
@IsInt()
@Max(5)
@Min(0)
@Min(-1)
rating?: number;
}

View file

@ -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', () => {

View file

@ -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
View file

@ -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",

View file

@ -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",

View file

@ -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')}

View file

@ -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);
}
};

View file

@ -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}

View file

@ -157,7 +157,6 @@ async function fileUploader(
}
} catch (error) {
console.error(`Error calculating sha1 file=${assetFile.name})`, error);
throw error;
}
}

View file

@ -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({