Compare commits

...

7 Commits

Author SHA1 Message Date
github-actions bee49cef02 chore: version v2.7.4 2026-04-10 16:32:26 +00:00
shenlong 6d0c6a4008 chore: pump cronet version (#27685)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-04-10 16:29:05 +00:00
Luis Nachtigall 8a975e5ea9 refactor(mobile): cleanup iOS image loading pipeline (#27672)
* refactor: replace DispatchQueue + DispatchSemaphore with OperationQueue for image processing

* implement RequestRegistry and UnfairLock for managing cancellable requests

* implement requests registry for local and remote image processing

* remove Cancellable protocol and cancel method from request registry

* refactor: introduce ImageRequest base class with unified cancellation and finish helpers

* refactor: add get method to RequestRegistry and streamline request removal in image processing

* add guard to cancel to prevent double onCancel calls

* fix duplicate code merge issue

* refactor(ios): enhance finish method to return callback status

* remove unfitting methods form ImageRequest.swift and fix memory issue

* revert bad merge

* refactor(ios): resolve cancellation issues

* refactor(ios): streamline image request completion handling

* add return statements

* refactor(ios): simplify image request cancellation and registry handling

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-04-10 10:56:35 -05:00
Luis Nachtigall d39e7da10d fix(mobile): fix flutter cache eviction on thumbnails (#27663)
* fix: add markFinished parameter to loadRequest and loadCodecRequest methods

* update loadRequest and loadCodecRequest methods to use isFinal

* Apply suggestions from code review

Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>

* remove redundant check

* fix: ensure isFinished is set correctly during cache eviction

* formatting

---------

Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
2026-04-10 10:28:55 -05:00
Daniel Dietzler bc400d68ac chore: move .tsbuildinfo file to dist folder (#27682) 2026-04-10 16:02:25 +02:00
renovate[bot] d7f038ec60 chore(deps): update dependency eslint-plugin-unicorn to v64 (#27575)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-10 10:23:42 +00:00
Mees Frensel 26957f37ce fix(server): hide original filename when not showing metadata (#27581) 2026-04-10 12:07:18 +02:00
31 changed files with 193 additions and 342 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.7.3",
"version": "2.7.4",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",
@@ -28,7 +28,7 @@
"eslint": "^10.0.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^63.0.0",
"eslint-plugin-unicorn": "^64.0.0",
"globals": "^17.0.0",
"mock-fs": "^5.2.0",
"prettier": "^3.7.4",
+1
View File
@@ -19,6 +19,7 @@
"paths": {
"src/*": ["./src/*"],
},
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo",
"types": ["vitest/globals"]
},
"exclude": ["dist", "node_modules", "vite.config.ts"]
+2 -2
View File
@@ -1,7 +1,7 @@
[
{
"label": "v2.7.3",
"url": "https://docs.v2.7.3.archive.immich.app"
"label": "v2.7.4",
"url": "https://docs.v2.7.4.archive.immich.app"
},
{
"label": "v2.6.3",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "2.7.3",
"version": "2.7.4",
"description": "",
"main": "index.js",
"type": "module",
@@ -40,7 +40,7 @@
"eslint": "^10.0.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^63.0.0",
"eslint-plugin-unicorn": "^64.0.0",
"exiftool-vendored": "^35.0.0",
"globals": "^17.0.0",
"luxon": "^3.4.4",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "immich-i18n",
"version": "2.7.3",
"version": "2.7.4",
"private": true,
"scripts": {
"format": "prettier --cache --check .",
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "immich-ml"
version = "2.7.3"
version = "2.7.4"
description = ""
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
requires-python = ">=3.11,<4.0"
+1 -1
View File
@@ -898,7 +898,7 @@ wheels = [
[[package]]
name = "immich-ml"
version = "2.7.3"
version = "2.7.4"
source = { editable = "." }
dependencies = [
{ name = "aiocache" },
+2 -2
View File
@@ -113,8 +113,8 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
implementation 'org.chromium.net:cronet-embedded:143.7445.0'
implementation("androidx.media3:media3-datasource-okhttp:1.9.2")
implementation("androidx.media3:media3-datasource-cronet:1.9.2")
implementation("androidx.media3:media3-datasource-okhttp:1.10.0")
implementation("androidx.media3:media3-datasource-cronet:1.10.0")
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
implementation "androidx.work:work-runtime-ktx:$work_version"
implementation "androidx.concurrent:concurrent-futures:$concurrent_version"
+2 -2
View File
@@ -35,8 +35,8 @@ platform :android do
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.version.code" => 3044,
"android.injected.version.name" => "2.7.3",
"android.injected.version.code" => 3045,
"android.injected.version.name" => "2.7.4",
}
)
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')
@@ -1,5 +1,32 @@
import Foundation
class ImageRequest: @unchecked Sendable {
private struct State: Sendable {
var isCancelled = false
}
let completion: @Sendable (Result<[String: Int64]?, any Error>) -> Void
private let state: Mutex<State>
var isCancelled: Bool {
get {
state.withLock { $0.isCancelled }
}
set {
state.withLock { $0.isCancelled = newValue }
}
}
init(completion: @escaping @Sendable (Result<[String: Int64]?, any Error>) -> Void) {
self.state = Mutex(State())
self.completion = completion
}
func cancel() {
isCancelled = true
}
}
struct RequestRegistry<T: AnyObject & Sendable>: ~Copyable, Sendable {
private let requests = Mutex<[Int64: T]>([:])
+18 -36
View File
@@ -3,21 +3,6 @@ import Flutter
import MobileCoreServices
import Photos
class LocalImageRequest {
weak var operation: Operation?
var isCancelled = false
let callback: (Result<[String: Int64]?, any Error>) -> Void
init(callback: @escaping (Result<[String: Int64]?, any Error>) -> Void) {
self.callback = callback
}
func cancel() {
isCancelled = true
operation?.cancel()
}
}
class LocalImageApiImpl: LocalImageApi {
private static let imageManager = PHImageManager.default()
private static let fetchOptions = {
@@ -36,9 +21,9 @@ class LocalImageApiImpl: LocalImageApi {
return requestOptions
}()
private static let registry = RequestRegistry<LocalImageRequest>()
private static let registry = RequestRegistry<ImageRequest>()
private static var rgbaFormat = vImage_CGImageFormat(
private static let rgbaFormat = vImage_CGImageFormat(
bitsPerComponent: 8,
bitsPerPixel: 32,
colorSpace: CGColorSpaceCreateDeviceRGB(),
@@ -67,21 +52,20 @@ class LocalImageApiImpl: LocalImageApi {
}
func requestImage(assetId: String, requestId: Int64, width: Int64, height: Int64, isVideo: Bool, preferEncoded: Bool, completion: @escaping (Result<[String: Int64]?, any Error>) -> Void) {
let request = LocalImageRequest(callback: completion)
let request = ImageRequest(completion: completion)
let operation = BlockOperation {
if request.isCancelled {
return completion(ImageProcessing.cancelledResult)
return request.completion(ImageProcessing.cancelledResult)
}
guard let asset = Self.requestAsset(assetId: assetId)
else {
Self.registry.remove(requestId: requestId)
completion(.failure(PigeonError(code: "", message: "Could not get asset data for \(assetId)", details: nil)))
return
return request.completion(.failure(PigeonError(code: "", message: "Could not get asset data for \(assetId)", details: nil)))
}
if request.isCancelled {
return completion(ImageProcessing.cancelledResult)
return request.completion(ImageProcessing.cancelledResult)
}
if preferEncoded {
@@ -100,12 +84,12 @@ class LocalImageApiImpl: LocalImageApi {
)
if request.isCancelled {
return completion(ImageProcessing.cancelledResult)
return request.completion(ImageProcessing.cancelledResult)
}
guard let data = imageData else {
Self.registry.remove(requestId: requestId)
return completion(.failure(PigeonError(code: "", message: "Could not get image data for \(assetId)", details: nil)))
return request.completion(.failure(PigeonError(code: "", message: "Could not get image data for \(assetId)", details: nil)))
}
let length = data.count
@@ -114,15 +98,14 @@ class LocalImageApiImpl: LocalImageApi {
if request.isCancelled {
free(pointer)
return completion(ImageProcessing.cancelledResult)
return request.completion(ImageProcessing.cancelledResult)
}
request.callback(.success([
Self.registry.remove(requestId: requestId)
return request.completion(.success([
"pointer": Int64(Int(bitPattern: pointer)),
"length": Int64(length),
]))
Self.registry.remove(requestId: requestId)
return
}
var image: UIImage?
@@ -137,17 +120,17 @@ class LocalImageApiImpl: LocalImageApi {
)
if request.isCancelled {
return completion(ImageProcessing.cancelledResult)
return request.completion(ImageProcessing.cancelledResult)
}
guard let image = image,
let cgImage = image.cgImage else {
Self.registry.remove(requestId: requestId)
return completion(.failure(PigeonError(code: "", message: "Could not get pixel data for \(assetId)", details: nil)))
return request.completion(.failure(PigeonError(code: "", message: "Could not get pixel data for \(assetId)", details: nil)))
}
if request.isCancelled {
return completion(ImageProcessing.cancelledResult)
return request.completion(ImageProcessing.cancelledResult)
}
do {
@@ -155,23 +138,22 @@ class LocalImageApiImpl: LocalImageApi {
if request.isCancelled {
buffer.free()
return completion(ImageProcessing.cancelledResult)
return request.completion(ImageProcessing.cancelledResult)
}
request.callback(.success([
Self.registry.remove(requestId: requestId)
return request.completion(.success([
"pointer": Int64(Int(bitPattern: buffer.data)),
"width": Int64(buffer.width),
"height": Int64(buffer.height),
"rowBytes": Int64(buffer.rowBytes),
]))
Self.registry.remove(requestId: requestId)
} catch {
Self.registry.remove(requestId: requestId)
return completion(.failure(PigeonError(code: "", message: "Failed to convert image for \(assetId): \(error)", details: nil)))
return request.completion(.failure(PigeonError(code: "", message: "Failed to convert image for \(assetId): \(error)", details: nil)))
}
}
request.operation = operation
Self.registry.add(requestId: requestId, request: request)
ImageProcessing.queue.addOperation(operation)
}
+46 -51
View File
@@ -3,27 +3,24 @@ import Flutter
import MobileCoreServices
import Photos
class RemoteImageRequest {
weak var task: URLSessionDataTask?
final class RemoteImageRequest: ImageRequest {
var task: URLSessionDataTask?
let id: Int64
var isCancelled = false
let completion: (Result<[String: Int64]?, any Error>) -> Void
init(id: Int64, task: URLSessionDataTask, completion: @escaping (Result<[String: Int64]?, any Error>) -> Void) {
init(id: Int64, completion: @escaping @Sendable (Result<[String: Int64]?, any Error>) -> Void) {
self.id = id
self.task = task
self.completion = completion
super.init(completion: completion)
}
func cancel() {
isCancelled = true
override func cancel() {
super.cancel()
task?.cancel()
}
}
class RemoteImageApiImpl: NSObject, RemoteImageApi {
private static let registry = RequestRegistry<RemoteImageRequest>()
private static var rgbaFormat = vImage_CGImageFormat(
private static let rgbaFormat = vImage_CGImageFormat(
bitsPerComponent: 8,
bitsPerPixel: 32,
colorSpace: CGColorSpaceCreateDeviceRGB(),
@@ -41,62 +38,58 @@ class RemoteImageApiImpl: NSObject, RemoteImageApi {
var urlRequest = URLRequest(url: URL(string: url)!)
urlRequest.cachePolicy = .returnCacheDataElseLoad
let request = RemoteImageRequest(id: requestId, completion: completion)
let task = URLSessionManager.shared.session.dataTask(with: urlRequest) { data, response, error in
Self.handleCompletion(requestId: requestId, encoded: preferEncoded, data: data, response: response, error: error)
Self.handleCompletion(request: request, encoded: preferEncoded, data: data, response: response, error: error)
}
let request = RemoteImageRequest(id: requestId, task: task, completion: completion)
request.task = task
Self.registry.add(requestId: requestId, request: request)
task.resume()
}
private static func handleCompletion(requestId: Int64, encoded: Bool, data: Data?, response: URLResponse?, error: Error?) {
guard let request = registry.remove(requestId: requestId) else {
return
}
if let error = error {
if request.isCancelled || (error as NSError).code == NSURLErrorCancelled {
return request.completion(ImageProcessing.cancelledResult)
}
return request.completion(.failure(error))
}
private static func handleCompletion(request: RemoteImageRequest, encoded: Bool, data: Data?, response: URLResponse?, error: Error?) {
if request.isCancelled {
return request.completion(ImageProcessing.cancelledResult)
}
if let error = error {
registry.remove(requestId: request.id)
return request.completion(.failure(error))
}
guard let data = data else {
registry.remove(requestId: request.id)
return request.completion(.failure(PigeonError(code: "", message: "No data received", details: nil)))
}
if encoded {
let length = data.count
let pointer = malloc(length)!
data.copyBytes(to: pointer.assumingMemoryBound(to: UInt8.self), count: length)
if request.isCancelled {
free(pointer)
return request.completion(ImageProcessing.cancelledResult)
}
registry.remove(requestId: request.id)
return request.completion(
.success([
"pointer": Int64(Int(bitPattern: pointer)),
"length": Int64(length),
]))
}
ImageProcessing.queue.addOperation {
if request.isCancelled {
return request.completion(ImageProcessing.cancelledResult)
}
// Return raw encoded bytes when requested (for animated images)
if encoded {
let length = data.count
let pointer = malloc(length)!
data.copyBytes(to: pointer.assumingMemoryBound(to: UInt8.self), count: length)
if request.isCancelled {
free(pointer)
return request.completion(ImageProcessing.cancelledResult)
}
return request.completion(
.success([
"pointer": Int64(Int(bitPattern: pointer)),
"length": Int64(length),
]))
}
guard let imageSource = CGImageSourceCreateWithData(data as CFData, nil),
let cgImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, decodeOptions) else {
registry.remove(requestId: request.id)
return request.completion(.failure(PigeonError(code: "", message: "Failed to decode image for request", details: nil)))
}
@@ -112,14 +105,16 @@ class RemoteImageApiImpl: NSObject, RemoteImageApi {
return request.completion(ImageProcessing.cancelledResult)
}
request.completion(
.success([
"pointer": Int64(Int(bitPattern: buffer.data)),
"width": Int64(buffer.width),
"height": Int64(buffer.height),
"rowBytes": Int64(buffer.rowBytes),
]))
registry.remove(requestId: request.id)
return request.completion(
.success([
"pointer": Int64(Int(bitPattern: buffer.data)),
"width": Int64(buffer.width),
"height": Int64(buffer.height),
"rowBytes": Int64(buffer.rowBytes),
]))
} catch {
registry.remove(requestId: request.id)
return request.completion(.failure(PigeonError(code: "", message: "Failed to convert image for request: \(error)", details: nil)))
}
}
+1 -1
View File
@@ -80,7 +80,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.7.3</string>
<string>2.7.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@@ -51,7 +51,7 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
return null;
}
Stream<ImageInfo> loadRequest(ImageRequest request, ImageDecoderCallback decode, {bool evictOnError = true}) async* {
Stream<ImageInfo> loadRequest(ImageRequest request, ImageDecoderCallback decode, {required bool isFinal}) async* {
if (isCancelled) {
this.request = null;
return;
@@ -59,21 +59,18 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
try {
final image = await request.load(decode);
if (isCancelled) {
return;
}
if (image == null && evictOnError) {
PaintingBinding.instance.imageCache.evict(this);
return;
} else if (image == null) {
if (isCancelled || image == null) {
image?.dispose();
return;
}
isFinished = isFinal;
yield image;
} catch (e, stack) {
if (isCancelled) {
return;
}
if (evictOnError) {
if (isFinal) {
isFinished = true;
PaintingBinding.instance.imageCache.evict(this);
rethrow;
}
@@ -83,7 +80,7 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
}
}
Future<ui.Codec?> loadCodecRequest(ImageRequest request) async {
Future<ui.Codec?> loadCodecRequest(ImageRequest request, {required bool isFinal}) async {
if (isCancelled) {
this.request = null;
return null;
@@ -91,20 +88,19 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
try {
final codec = await request.loadCodec();
if (isCancelled) {
if (isCancelled || codec == null) {
codec?.dispose();
return null;
}
if (codec == null) {
PaintingBinding.instance.imageCache.evict(this);
return null;
}
isFinished = isFinal;
return codec;
} catch (e) {
if (!isCancelled) {
if (isFinal) {
isFinished = true;
PaintingBinding.instance.imageCache.evict(this);
rethrow;
}
rethrow;
return null;
} finally {
this.request = null;
}
@@ -36,7 +36,7 @@ class LocalThumbProvider extends CancellableImageProvider<LocalThumbProvider>
Stream<ImageInfo> _codec(LocalThumbProvider key, ImageDecoderCallback decode) {
final request = this.request = LocalImageRequest(localId: key.id, size: key.size, assetType: key.assetType);
return loadRequest(request, decode);
return loadRequest(request, decode, isFinal: true);
}
@override
@@ -103,16 +103,16 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
return;
}
final loadOriginal = Store.get(StoreKey.loadOriginal, false);
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
var request = this.request = LocalImageRequest(
localId: key.id,
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
assetType: key.assetType,
);
yield* loadRequest(request, decode);
yield* loadRequest(request, decode, isFinal: !loadOriginal);
if (!Store.get(StoreKey.loadOriginal, false)) {
isFinished = true;
if (!loadOriginal) {
return;
}
@@ -122,8 +122,7 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
request = this.request = LocalImageRequest(localId: key.id, assetType: key.assetType, size: Size.zero);
yield* loadRequest(request, decode);
isFinished = true;
yield* loadRequest(request, decode, isFinal: true);
}
Stream<Object> _animatedCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async* {
@@ -139,7 +138,7 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
assetType: key.assetType,
);
yield* loadRequest(previewRequest, decode);
yield* loadRequest(previewRequest, decode, isFinal: false);
if (isCancelled) {
return;
@@ -147,13 +146,12 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
// always try original for animated, since previews don't support animation
final originalRequest = request = LocalImageRequest(localId: key.id, size: Size.zero, assetType: key.assetType);
final codec = await loadCodecRequest(originalRequest);
final codec = await loadCodecRequest(originalRequest, isFinal: true);
if (codec == null) {
if (isCancelled) return;
throw StateError('Failed to load animated codec for local asset ${key.id}');
}
yield codec;
isFinished = true;
}
@override
@@ -38,7 +38,7 @@ class RemoteImageProvider extends CancellableImageProvider<RemoteImageProvider>
Stream<ImageInfo> _codec(RemoteImageProvider key, ImageDecoderCallback decode) {
final request = this.request = RemoteImageRequest(uri: key.url);
return loadRequest(request, decode);
return loadRequest(request, decode, isFinal: true);
}
@override
@@ -112,10 +112,9 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
uri: getThumbnailUrlForRemoteId(key.assetId, type: AssetMediaSize.preview, thumbhash: key.thumbhash),
);
final loadOriginal = assetType == AssetType.image && AppSetting.get(Setting.loadOriginal);
yield* loadRequest(previewRequest, decode, evictOnError: !loadOriginal);
yield* loadRequest(previewRequest, decode, isFinal: !loadOriginal);
if (!loadOriginal) {
isFinished = true;
return;
}
@@ -124,8 +123,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
}
final originalRequest = request = RemoteImageRequest(uri: getOriginalUrlForRemoteId(key.assetId));
yield* loadRequest(originalRequest, decode);
isFinished = true;
yield* loadRequest(originalRequest, decode, isFinal: true);
}
Stream<Object> _animatedCodec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* {
@@ -138,7 +136,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
final previewRequest = request = RemoteImageRequest(
uri: getThumbnailUrlForRemoteId(key.assetId, type: AssetMediaSize.preview, thumbhash: key.thumbhash),
);
yield* loadRequest(previewRequest, decode, evictOnError: false);
yield* loadRequest(previewRequest, decode, isFinal: false);
if (isCancelled) {
return;
@@ -146,7 +144,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
// always try original for animated, since previews don't support animation
final originalRequest = request = RemoteImageRequest(uri: getOriginalUrlForRemoteId(key.assetId));
final codec = await loadCodecRequest(originalRequest);
final codec = await loadCodecRequest(originalRequest, isFinal: true);
if (codec == null) {
if (isCancelled) {
return;
@@ -154,7 +152,6 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
throw StateError('Failed to load animated codec for asset ${key.assetId}');
}
yield codec;
isFinished = true;
}
@override
@@ -22,7 +22,7 @@ class ThumbHashProvider extends CancellableImageProvider<ThumbHashProvider>
Stream<ImageInfo> _loadCodec(ThumbHashProvider key, ImageDecoderCallback decode) {
final request = this.request = ThumbhashImageRequest(thumbhash: key.thumbHash);
return loadRequest(request, decode);
return loadRequest(request, decode, isFinal: true);
}
@override
+1 -1
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.7.3
- API version: 2.7.4
- Generator version: 7.8.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen
+1 -1
View File
@@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone
publish_to: 'none'
version: 2.7.3+3044
version: 2.7.4+3045
environment:
sdk: '>=3.8.0 <4.0.0'
+1 -1
View File
@@ -15225,7 +15225,7 @@
"info": {
"title": "Immich",
"description": "Immich API",
"version": "2.7.3",
"version": "2.7.4",
"contact": {}
},
"tags": [
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@immich/sdk",
"version": "2.7.3",
"version": "2.7.4",
"description": "Auto-generated TypeScript SDK for the Immich API",
"type": "module",
"main": "./build/index.js",
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* Immich
* 2.7.3
* 2.7.4
* DO NOT MODIFY - This file has been generated using oazapfts.
* See https://www.npmjs.com/package/oazapfts
*/
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "immich-monorepo",
"version": "2.7.3",
"version": "2.7.4",
"description": "Monorepo for Immich",
"private": true,
"packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
+27 -195
View File
@@ -87,8 +87,8 @@ importers:
specifier: ^5.1.3
version: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.1.0(jiti@2.6.1)))(eslint@10.1.0(jiti@2.6.1))(prettier@3.8.1)
eslint-plugin-unicorn:
specifier: ^63.0.0
version: 63.0.0(eslint@10.1.0(jiti@2.6.1))
specifier: ^64.0.0
version: 64.0.0(eslint@10.1.0(jiti@2.6.1))
globals:
specifier: ^17.0.0
version: 17.4.0
@@ -241,8 +241,8 @@ importers:
specifier: ^5.1.3
version: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.1.0(jiti@2.6.1)))(eslint@10.1.0(jiti@2.6.1))(prettier@3.8.1)
eslint-plugin-unicorn:
specifier: ^63.0.0
version: 63.0.0(eslint@10.1.0(jiti@2.6.1))
specifier: ^64.0.0
version: 64.0.0(eslint@10.1.0(jiti@2.6.1))
exiftool-vendored:
specifier: ^35.0.0
version: 35.15.1
@@ -278,7 +278,7 @@ importers:
version: 6.0.2
typescript-eslint:
specifier: ^8.28.0
version: 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
version: 8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
utimes:
specifier: ^5.2.1
version: 5.2.1(encoding@0.1.13)
@@ -681,8 +681,8 @@ importers:
specifier: ^5.1.3
version: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.1.0(jiti@2.6.1)))(eslint@10.1.0(jiti@2.6.1))(prettier@3.8.1)
eslint-plugin-unicorn:
specifier: ^63.0.0
version: 63.0.0(eslint@10.1.0(jiti@2.6.1))
specifier: ^64.0.0
version: 64.0.0(eslint@10.1.0(jiti@2.6.1))
globals:
specifier: ^17.0.0
version: 17.4.0
@@ -718,7 +718,7 @@ importers:
version: 6.0.2
typescript-eslint:
specifier: ^8.28.0
version: 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
version: 8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
unplugin-swc:
specifier: ^1.4.5
version: 1.5.9(@swc/core@1.15.18(@swc/helpers@0.5.17))(rollup@4.55.1)
@@ -928,8 +928,8 @@ importers:
specifier: ^3.12.4
version: 3.16.0(eslint@10.1.0(jiti@2.6.1))(svelte@5.54.1)
eslint-plugin-unicorn:
specifier: ^63.0.0
version: 63.0.0(eslint@10.1.0(jiti@2.6.1))
specifier: ^64.0.0
version: 64.0.0(eslint@10.1.0(jiti@2.6.1))
factory.ts:
specifier: ^1.4.1
version: 1.4.2
@@ -968,7 +968,7 @@ importers:
version: 6.0.2
typescript-eslint:
specifier: ^8.45.0
version: 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
version: 8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
vite:
specifier: ^8.0.0
version: 8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3)
@@ -5233,14 +5233,6 @@ packages:
'@types/yargs@17.0.35':
resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==}
'@typescript-eslint/eslint-plugin@8.57.1':
resolution: {integrity: sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.57.1
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/eslint-plugin@8.58.0':
resolution: {integrity: sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -5249,13 +5241,6 @@ packages:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/parser@8.57.1':
resolution: {integrity: sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/parser@8.58.0':
resolution: {integrity: sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -5263,45 +5248,22 @@ packages:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/project-service@8.57.1':
resolution: {integrity: sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/project-service@8.58.0':
resolution: {integrity: sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/scope-manager@8.57.1':
resolution: {integrity: sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/scope-manager@8.58.0':
resolution: {integrity: sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.57.1':
resolution: {integrity: sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/tsconfig-utils@8.58.0':
resolution: {integrity: sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/type-utils@8.57.1':
resolution: {integrity: sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/type-utils@8.58.0':
resolution: {integrity: sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -5309,33 +5271,16 @@ packages:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/types@8.57.1':
resolution: {integrity: sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/types@8.58.0':
resolution: {integrity: sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.57.1':
resolution: {integrity: sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/typescript-estree@8.58.0':
resolution: {integrity: sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/utils@8.57.1':
resolution: {integrity: sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/utils@8.58.0':
resolution: {integrity: sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -5343,10 +5288,6 @@ packages:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/visitor-keys@8.57.1':
resolution: {integrity: sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/visitor-keys@8.58.0':
resolution: {integrity: sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -6129,8 +6070,8 @@ packages:
resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
engines: {node: '>=8'}
ci-info@4.3.1:
resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==}
ci-info@4.4.0:
resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==}
engines: {node: '>=8'}
citty@0.1.6:
@@ -6411,8 +6352,8 @@ packages:
peerDependencies:
webpack: ^5.1.0
core-js-compat@3.47.0:
resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==}
core-js-compat@3.49.0:
resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==}
core-js-pure@3.47.0:
resolution: {integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==}
@@ -7235,8 +7176,8 @@ packages:
svelte:
optional: true
eslint-plugin-unicorn@63.0.0:
resolution: {integrity: sha512-Iqecl9118uQEXYh7adylgEmGfkn5es3/mlQTLLkd4pXkIk9CTGrAbeUux+YljSa2ohXCBmQQ0+Ej1kZaFgcfkA==}
eslint-plugin-unicorn@64.0.0:
resolution: {integrity: sha512-rNZwalHh8i0UfPlhNwg5BTUO1CMdKNmjqe+TgzOTZnpKoi8VBgsW7u9qCHIdpxEzZ1uwrJrPF0uRb7l//K38gA==}
engines: {node: ^20.10.0 || >=21.0.0}
peerDependencies:
eslint: '>=9.38.0'
@@ -11755,13 +11696,6 @@ packages:
typedarray@0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
typescript-eslint@8.57.1:
resolution: {integrity: sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
typescript-eslint@8.58.0:
resolution: {integrity: sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -13438,7 +13372,7 @@ snapshots:
babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.5)
babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5)
babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.5)
core-js-compat: 3.47.0
core-js-compat: 3.49.0
semver: 6.3.1
transitivePeerDependencies:
- supports-color
@@ -15490,8 +15424,8 @@ snapshots:
'@koddsson/eslint-plugin-tscompat@0.2.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)':
dependencies:
'@mdn/browser-compat-data': 6.1.5
'@typescript-eslint/type-utils': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
'@typescript-eslint/utils': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
'@typescript-eslint/type-utils': 8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
'@typescript-eslint/utils': 8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
browserslist: 4.28.1
transitivePeerDependencies:
- eslint
@@ -17526,22 +17460,6 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.3
'@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
'@typescript-eslint/scope-manager': 8.57.1
'@typescript-eslint/type-utils': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
'@typescript-eslint/utils': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
'@typescript-eslint/visitor-keys': 8.57.1
eslint: 10.1.0(jiti@2.6.1)
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@6.0.2)
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)':
dependencies:
'@eslint-community/regexpp': 4.12.2
@@ -17558,18 +17476,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)':
dependencies:
'@typescript-eslint/scope-manager': 8.57.1
'@typescript-eslint/types': 8.57.1
'@typescript-eslint/typescript-estree': 8.57.1(typescript@6.0.2)
'@typescript-eslint/visitor-keys': 8.57.1
debug: 4.4.3
eslint: 10.1.0(jiti@2.6.1)
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)':
dependencies:
'@typescript-eslint/scope-manager': 8.58.0
@@ -17582,15 +17488,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.57.1(typescript@6.0.2)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.57.1(typescript@6.0.2)
'@typescript-eslint/types': 8.57.1
debug: 4.4.3
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.58.0(typescript@6.0.2)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.58.0(typescript@6.0.2)
@@ -17600,36 +17497,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/scope-manager@8.57.1':
dependencies:
'@typescript-eslint/types': 8.57.1
'@typescript-eslint/visitor-keys': 8.57.1
'@typescript-eslint/scope-manager@8.58.0':
dependencies:
'@typescript-eslint/types': 8.58.0
'@typescript-eslint/visitor-keys': 8.58.0
'@typescript-eslint/tsconfig-utils@8.57.1(typescript@6.0.2)':
dependencies:
typescript: 6.0.2
'@typescript-eslint/tsconfig-utils@8.58.0(typescript@6.0.2)':
dependencies:
typescript: 6.0.2
'@typescript-eslint/type-utils@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)':
dependencies:
'@typescript-eslint/types': 8.57.1
'@typescript-eslint/typescript-estree': 8.57.1(typescript@6.0.2)
'@typescript-eslint/utils': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
debug: 4.4.3
eslint: 10.1.0(jiti@2.6.1)
ts-api-utils: 2.5.0(typescript@6.0.2)
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/type-utils@8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)':
dependencies:
'@typescript-eslint/types': 8.58.0
@@ -17642,25 +17518,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/types@8.57.1': {}
'@typescript-eslint/types@8.58.0': {}
'@typescript-eslint/typescript-estree@8.57.1(typescript@6.0.2)':
dependencies:
'@typescript-eslint/project-service': 8.57.1(typescript@6.0.2)
'@typescript-eslint/tsconfig-utils': 8.57.1(typescript@6.0.2)
'@typescript-eslint/types': 8.57.1
'@typescript-eslint/visitor-keys': 8.57.1
debug: 4.4.3
minimatch: 10.2.4
semver: 7.7.4
tinyglobby: 0.2.15
ts-api-utils: 2.5.0(typescript@6.0.2)
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/typescript-estree@8.58.0(typescript@6.0.2)':
dependencies:
'@typescript-eslint/project-service': 8.58.0(typescript@6.0.2)
@@ -17676,17 +17535,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@2.6.1))
'@typescript-eslint/scope-manager': 8.57.1
'@typescript-eslint/types': 8.57.1
'@typescript-eslint/typescript-estree': 8.57.1(typescript@6.0.2)
eslint: 10.1.0(jiti@2.6.1)
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@2.6.1))
@@ -17698,11 +17546,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/visitor-keys@8.57.1':
dependencies:
'@typescript-eslint/types': 8.57.1
eslint-visitor-keys: 5.0.1
'@typescript-eslint/visitor-keys@8.58.0':
dependencies:
'@typescript-eslint/types': 8.58.0
@@ -18223,7 +18066,7 @@ snapshots:
dependencies:
'@babel/core': 7.28.5
'@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5)
core-js-compat: 3.47.0
core-js-compat: 3.49.0
transitivePeerDependencies:
- supports-color
@@ -18638,7 +18481,7 @@ snapshots:
ci-info@3.9.0: {}
ci-info@4.3.1: {}
ci-info@4.4.0: {}
citty@0.1.6:
dependencies:
@@ -18890,7 +18733,7 @@ snapshots:
serialize-javascript: 6.0.2
webpack: 5.104.1
core-js-compat@3.47.0:
core-js-compat@3.49.0:
dependencies:
browserslist: 4.28.1
@@ -19844,17 +19687,17 @@ snapshots:
transitivePeerDependencies:
- ts-node
eslint-plugin-unicorn@63.0.0(eslint@10.1.0(jiti@2.6.1)):
eslint-plugin-unicorn@64.0.0(eslint@10.1.0(jiti@2.6.1)):
dependencies:
'@babel/helper-validator-identifier': 7.28.5
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@2.6.1))
change-case: 5.4.4
ci-info: 4.3.1
ci-info: 4.4.0
clean-regexp: 1.0.0
core-js-compat: 3.47.0
core-js-compat: 3.49.0
eslint: 10.1.0(jiti@2.6.1)
find-up-simple: 1.0.1
globals: 16.5.0
globals: 17.4.0
indent-string: 5.0.0
is-builtin-module: 5.0.0
jsesc: 3.1.0
@@ -25358,17 +25201,6 @@ snapshots:
typedarray@0.0.6: {}
typescript-eslint@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2):
dependencies:
'@typescript-eslint/eslint-plugin': 8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
'@typescript-eslint/parser': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
'@typescript-eslint/typescript-estree': 8.57.1(typescript@6.0.2)
'@typescript-eslint/utils': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
eslint: 10.1.0(jiti@2.6.1)
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
typescript-eslint@8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2):
dependencies:
'@typescript-eslint/eslint-plugin': 8.58.0(@typescript-eslint/parser@8.58.0(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.1.0(jiti@2.6.1))(typescript@6.0.2)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "immich",
"version": "2.7.3",
"version": "2.7.4",
"description": "",
"author": "",
"private": true,
@@ -154,7 +154,7 @@
"eslint": "^10.0.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^63.0.0",
"eslint-plugin-unicorn": "^64.0.0",
"globals": "^17.0.0",
"mock-fs": "^5.2.0",
"node-gyp": "^12.0.0",
@@ -58,6 +58,7 @@ export class OcrRepository {
})
upsert(assetId: string, ocrDataList: Insertable<AssetOcrTable>[], searchText: string) {
let query = this.db.with('deleted_ocr', (db) => db.deleteFrom('asset_ocr').where('assetId', '=', assetId));
// eslint-disable-next-line unicorn/prefer-ternary
if (ocrDataList.length > 0) {
(query as any) = query
.with('inserted_ocr', (db) => db.insertInto('asset_ocr').values(ocrDataList))
@@ -692,6 +692,24 @@ describe(AssetMediaService.name, () => {
);
expect(mocks.asset.getForThumbnail).toHaveBeenCalledWith(asset.id, AssetFileType.Thumbnail, true);
});
it('should not include original filename if requested using a shared link with showExif false', async () => {
const asset = AssetFactory.from().file({ type: AssetFileType.Preview }).build();
mocks.access.asset.checkSharedLinkAccess.mockResolvedValue(new Set([asset.id]));
mocks.asset.getForThumbnail.mockResolvedValue({ ...asset, path: asset.files[0].path });
const auth = AuthFactory.from().sharedLink({ showExif: false }).build();
await expect(sut.viewThumbnail(auth, asset.id, { size: AssetMediaSize.PREVIEW })).resolves.toEqual(
new ImmichFileResponse({
path: asset.files[0].path,
cacheControl: CacheControl.PrivateWithCache,
contentType: 'image/jpeg',
fileName: `${asset.id}_preview.jpg`,
}),
);
});
});
describe('playbackVideo', () => {
+3 -1
View File
@@ -257,7 +257,9 @@ export class AssetMediaService extends BaseService {
throw new NotFoundException('Asset media not found');
}
const fileName = `${getFileNameWithoutExtension(originalFileName)}_${size}${getFilenameExtension(path)}`;
const fileNameBase =
auth.sharedLink && !auth.sharedLink.showExif ? id : getFileNameWithoutExtension(originalFileName);
const fileName = `${fileNameBase}_${size}${getFilenameExtension(path)}`;
return new ImmichFileResponse({
fileName,
+1
View File
@@ -2,6 +2,7 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"tsBuildInfoFile": "./dist/tsconfig.build.tsbuildinfo",
},
"exclude": ["dist", "node_modules", "upload", "test", "e2e", "**/*spec.ts"]
}
+1
View File
@@ -24,6 +24,7 @@
"rootDir": ".",
"jsx": "react",
"types": ["vitest/globals"],
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo",
"noErrorTruncation": true
},
"exclude": ["dist", "node_modules", "upload"]
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "immich-web",
"version": "2.7.3",
"version": "2.7.4",
"license": "GNU Affero General Public License version 3",
"type": "module",
"scripts": {
@@ -91,7 +91,7 @@
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-compat": "^7.0.0",
"eslint-plugin-svelte": "^3.12.4",
"eslint-plugin-unicorn": "^63.0.0",
"eslint-plugin-unicorn": "^64.0.0",
"factory.ts": "^1.4.1",
"globals": "^17.0.0",
"happy-dom": "^20.0.0",