Compare commits

...

7 Commits

Author SHA1 Message Date
Jonathan Jogenfors
03797e8cf8 Merge branch 'main' of https://github.com/immich-app/immich into feat/web-chromecast-video-looping 2026-02-03 23:37:20 +01:00
Jason Rasmussen
3ea65f8d27 fix: album dto docs (#25873) 2026-02-03 21:05:18 +00:00
github-actions
38c1f0b1fd chore: version v2.5.3 2026-02-03 18:14:21 +00:00
Michel Heusschen
5212bca3d0 fix: reset zoom when navigating between assets (#25863) 2026-02-03 11:07:06 -06:00
Daniel Dietzler
2990bde0bb fix: metadata extraction race condition (#25866) 2026-02-03 11:03:27 -06:00
Jonathan Jogenfors
4cff61e035 Merge branch 'main' of https://github.com/immich-app/immich into feat/web-chromecast-video-looping 2026-02-02 22:55:46 +01:00
Jonathan Jogenfors
38b58902fe feat(web): loop chromecast video 2025-12-05 22:17:51 +01:00
22 changed files with 47 additions and 39 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.5.2",
"version": "2.5.3",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",

View File

@@ -1,7 +1,7 @@
[
{
"label": "v2.5.2",
"url": "https://docs.v2.5.2.archive.immich.app"
"label": "v2.5.3",
"url": "https://docs.v2.5.3.archive.immich.app"
},
{
"label": "v2.4.1",

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "immich-i18n",
"version": "2.5.2",
"version": "2.5.3",
"private": true,
"scripts": {
"format": "prettier --check .",

View File

@@ -1,6 +1,6 @@
[project]
name = "immich-ml"
version = "2.5.2"
version = "2.5.3"
description = ""
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
requires-python = ">=3.11,<4.0"

View File

@@ -882,7 +882,7 @@ wheels = [
[[package]]
name = "immich-ml"
version = "2.5.2"
version = "2.5.3"
source = { editable = "." }
dependencies = [
{ name = "aiocache" },

View File

@@ -35,8 +35,8 @@ platform :android do
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.version.code" => 3033,
"android.injected.version.name" => "2.5.2",
"android.injected.version.code" => 3034,
"android.injected.version.name" => "2.5.3",
}
)
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

@@ -80,7 +80,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.5.2</string>
<string>2.5.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 2.5.2
- API version: 2.5.3
- Generator version: 7.8.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen

View File

@@ -473,7 +473,7 @@ class AlbumsApi {
/// Filter albums containing this asset ID (ignores shared parameter)
///
/// * [bool] shared:
/// Filter by shared status: true = only shared, false = only own, undefined = all
/// Filter by shared status: true = only shared, false = not shared, undefined = all owned albums
Future<Response> getAllAlbumsWithHttpInfo({ String? assetId, bool? shared, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums';
@@ -516,7 +516,7 @@ class AlbumsApi {
/// Filter albums containing this asset ID (ignores shared parameter)
///
/// * [bool] shared:
/// Filter by shared status: true = only shared, false = only own, undefined = all
/// Filter by shared status: true = only shared, false = not shared, undefined = all owned albums
Future<List<AlbumResponseDto>?> getAllAlbums({ String? assetId, bool? shared, }) async {
final response = await getAllAlbumsWithHttpInfo( assetId: assetId, shared: shared, );
if (response.statusCode >= HttpStatus.badRequest) {

View File

@@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone
publish_to: 'none'
version: 2.5.2+3033
version: 2.5.3+3034
environment:
sdk: '>=3.8.0 <4.0.0'

View File

@@ -1618,7 +1618,7 @@
"name": "shared",
"required": false,
"in": "query",
"description": "Filter by shared status: true = only shared, false = only own, undefined = all",
"description": "Filter by shared status: true = only shared, false = not shared, undefined = all owned albums",
"schema": {
"type": "boolean"
}
@@ -15057,7 +15057,7 @@
"info": {
"title": "Immich",
"description": "Immich API",
"version": "2.5.2",
"version": "2.5.3",
"contact": {}
},
"tags": [

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/sdk",
"version": "2.5.2",
"version": "2.5.3",
"description": "Auto-generated TypeScript SDK for the Immich API",
"type": "module",
"main": "./build/index.js",

View File

@@ -1,6 +1,6 @@
/**
* Immich
* 2.5.2
* 2.5.3
* DO NOT MODIFY - This file has been generated using oazapfts.
* See https://www.npmjs.com/package/oazapfts
*/

View File

@@ -1,6 +1,6 @@
{
"name": "immich-monorepo",
"version": "2.5.2",
"version": "2.5.3",
"description": "Monorepo for Immich",
"private": true,
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48",

View File

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

View File

@@ -102,7 +102,7 @@ export class UpdateAlbumDto {
export class GetAlbumsDto {
@ValidateBoolean({
optional: true,
description: 'Filter by shared status: true = only shared, false = only own, undefined = all',
description: 'Filter by shared status: true = only shared, false = not shared, undefined = all owned albums',
})
shared?: boolean;

View File

@@ -307,7 +307,6 @@ export class MetadataService extends BaseService {
const assetHeight = isSidewards ? validate(width) : validate(height);
const promises: Promise<unknown>[] = [
this.assetRepository.upsertExif(exifData, { lockedPropertiesBehavior: 'skip' }),
this.assetRepository.update({
id: asset.id,
duration: this.getDuration(exifTags),
@@ -322,6 +321,7 @@ export class MetadataService extends BaseService {
}),
];
await this.assetRepository.upsertExif(exifData, { lockedPropertiesBehavior: 'skip' });
await this.applyTagList(asset);
if (this.isMotionPhoto(asset, exifTags)) {

View File

@@ -1,6 +1,6 @@
{
"name": "immich-web",
"version": "2.5.2",
"version": "2.5.3",
"license": "GNU Affero General Public License version 3",
"type": "module",
"scripts": {

View File

@@ -55,13 +55,10 @@
let loader = $state<HTMLImageElement>();
assetViewerManager.zoomState = {
currentRotation: 0,
currentZoom: 1,
enable: true,
currentPositionX: 0,
currentPositionY: 0,
};
$effect.pre(() => {
void asset.id;
untrack(() => assetViewerManager.resetZoomState());
});
onDestroy(() => {
$boundingBoxesArray = [];

View File

@@ -5,6 +5,14 @@ import type { ZoomImageWheelState } from '@zoom-image/core';
const isShowDetailPanel = new PersistedLocalStorage<boolean>('asset-viewer-state', false);
const createDefaultZoomState = (): ZoomImageWheelState => ({
currentRotation: 0,
currentZoom: 1,
enable: true,
currentPositionX: 0,
currentPositionY: 0,
});
export type Events = {
Zoom: [];
ZoomChange: [ZoomImageWheelState];
@@ -12,13 +20,7 @@ export type Events = {
};
export class AssetViewerManager extends BaseEventManager<Events> {
#zoomState = $state<ZoomImageWheelState>({
currentRotation: 0,
currentZoom: 1,
enable: true,
currentPositionX: 0,
currentPositionY: 0,
});
#zoomState = $state(createDefaultZoomState());
imgRef = $state<HTMLImageElement | undefined>();
isShowActivityPanel = $state(false);
@@ -67,6 +69,10 @@ export class AssetViewerManager extends BaseEventManager<Events> {
this.#zoomState = state;
}
resetZoomState() {
this.zoomState = createDefaultZoomState();
}
toggleActivityPanel() {
this.closeDetailPanel();
this.isShowActivityPanel = !this.isShowActivityPanel;

View File

@@ -115,12 +115,17 @@ export class GCastDestination implements ICastDestination {
// build the authenticated media request and send it to the cast device
const authenticatedUrl = `${mediaUrl}&sessionKey=${sessionKey}`;
const mediaInfo = new chrome.cast.media.MediaInfo(authenticatedUrl, contentType);
const request = new chrome.cast.media.LoadRequest(mediaInfo);
// Create a queue with a single item and set it to repeat
const queueItem = new chrome.cast.media.QueueItem(mediaInfo);
const queueLoadRequest = new chrome.cast.media.QueueLoadRequest([queueItem]);
queueLoadRequest.repeatMode = chrome.cast.media.RepeatMode.SINGLE;
const successCallback = this.onMediaDiscovered.bind(this, SESSION_DISCOVERY_CAUSE.LOAD_MEDIA);
this.currentUrl = mediaUrl;
return this.session.loadMedia(request, successCallback, this.onError.bind(this));
return this.session.queueLoad(queueLoadRequest, successCallback, this.onError.bind(this));
}
///