Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 425eb2c66e | |||
| e9616a3fbc |
@@ -38,11 +38,12 @@
|
|||||||
<a href="readme_i18n/README_th_TH.md">ภาษาไทย</a>
|
<a href="readme_i18n/README_th_TH.md">ภาษาไทย</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
> [!WARNING]
|
- ⚠️ The project is under **very active** development.
|
||||||
> ⚠️ Always follow [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan for your precious photos and videos!
|
- ⚠️ Expect bugs and breaking changes.
|
||||||
>
|
- ⚠️ **Do not use the app as the only way to store your photos and videos.**
|
||||||
|
- ⚠️ Always follow [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan for your precious photos and videos!
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> You can find the main documentation, including installation guides, at https://immich.app/.
|
> You can find the main documentation, including installation guides, at https://immich.app/.
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.95",
|
"version": "2.2.94",
|
||||||
"description": "Command Line Interface (CLI) for Immich",
|
"description": "Command Line Interface (CLI) for Immich",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": "./dist/index.js",
|
"exports": "./dist/index.js",
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
sidebar_position: 65
|
|
||||||
---
|
|
||||||
|
|
||||||
# One-Click [Cloud Service]
|
|
||||||
|
|
||||||
:::note
|
|
||||||
This version of Immich is provided via cloud service provider's one-click marketplaces. Hosting costs are set by the cloud service providers.
|
|
||||||
Support for these are provided by the individual cloud service providers.
|
|
||||||
|
|
||||||
**Please report issues to the corresponding [Github Repository][github].**
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Simply goto the providers marketplace and choose Immich, then follow the provided instructions.
|
|
||||||
|
|
||||||
## One-Click Immich marketplace providers
|
|
||||||
|
|
||||||
### DigitalOcean
|
|
||||||
|
|
||||||
https://marketplace.digitalocean.com/apps/immich
|
|
||||||
|
|
||||||
### Vultr
|
|
||||||
|
|
||||||
https://www.vultr.com/marketplace/apps/immich
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
For issues, open an issue on the associated [GitHub Repository][github].
|
|
||||||
|
|
||||||
[github]: https://github.com/imagegenius/docker-immich/
|
|
||||||
@@ -4,7 +4,9 @@ sidebar_position: 95
|
|||||||
|
|
||||||
# Upgrading
|
# Upgrading
|
||||||
|
|
||||||
:::tip Breaking changes
|
:::danger Read the release notes
|
||||||
|
Immich is currently under heavy development, which means you can expect [breaking changes][breaking] and bugs. You should read the release notes prior to updating and take special care when using automated tools like [Watchtower][watchtower].
|
||||||
|
|
||||||
You can see versions that had breaking changes [here][breaking].
|
You can see versions that had breaking changes [here][breaking].
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
Now that you have imported some pictures, you should setup server backups to preserve your memories.
|
Now that you have imported some pictures, you should setup server backups to preserve your memories.
|
||||||
You can do so by following our [backup guide](/administration/backup-and-restore.md).
|
You can do so by following our [backup guide](/administration/backup-and-restore.md).
|
||||||
|
|
||||||
:::info
|
:::danger
|
||||||
A 3-2-1 backup strategy is still crucial. The team has the responsibility to ensure that the application doesn’t cause loss of your precious memories; however, we cannot guarantee that hard drives will not fail, or an electrical event causes unexpected shutdown of your server/system, leading to data loss. Therefore, we still encourage users to follow best practices when safeguarding their data. Keep multiple copies of your most precious data: at least two local copies and one copy offsite in cold storage.
|
Immich is still under heavy development _and_ handles very important data.
|
||||||
|
It is essential that you set up good backups, and test them.
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const prism = require('prism-react-renderer');
|
|||||||
const config = {
|
const config = {
|
||||||
title: 'Immich',
|
title: 'Immich',
|
||||||
tagline: 'High performance self-hosted photo and video backup solution directly from your mobile phone',
|
tagline: 'High performance self-hosted photo and video backup solution directly from your mobile phone',
|
||||||
url: 'https://docs.immich.app',
|
url: 'https://immich.app',
|
||||||
baseUrl: '/',
|
baseUrl: '/',
|
||||||
onBrokenLinks: 'throw',
|
onBrokenLinks: 'throw',
|
||||||
onBrokenMarkdownLinks: 'warn',
|
onBrokenMarkdownLinks: 'warn',
|
||||||
@@ -65,6 +65,11 @@ const config = {
|
|||||||
themeConfig:
|
themeConfig:
|
||||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||||
({
|
({
|
||||||
|
announcementBar: {
|
||||||
|
id: 'site_announcement_immich',
|
||||||
|
content: `⚠️ The project is under <strong>very active</strong> development. Expect bugs and changes. Do not use it as <strong>the only way</strong> to store your photos and videos!`,
|
||||||
|
isCloseable: false,
|
||||||
|
},
|
||||||
docs: {
|
docs: {
|
||||||
sidebar: {
|
sidebar: {
|
||||||
autoCollapseCategories: false,
|
autoCollapseCategories: false,
|
||||||
|
|||||||
Vendored
-4
@@ -1,8 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"label": "v2.0.0",
|
|
||||||
"url": "https://docs.v2.0.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.144.1",
|
"label": "v1.144.1",
|
||||||
"url": "https://docs.v1.144.1.archive.immich.app"
|
"url": "https://docs.v1.144.1.archive.immich.app"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "2.0.0",
|
"version": "1.144.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "immich-ml"
|
name = "immich-ml"
|
||||||
version = "2.0.0"
|
version = "1.144.1"
|
||||||
description = ""
|
description = ""
|
||||||
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
|
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
|
||||||
requires-python = ">=3.10,<4.0"
|
requires-python = ">=3.10,<4.0"
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 3020,
|
"android.injected.version.code" => 3019,
|
||||||
"android.injected.version.name" => "2.0.0",
|
"android.injected.version.name" => "1.144.1",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
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')
|
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')
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ platform :ios do
|
|||||||
path: "./Runner.xcodeproj",
|
path: "./Runner.xcodeproj",
|
||||||
)
|
)
|
||||||
increment_version_number(
|
increment_version_number(
|
||||||
version_number: "2.0.0"
|
version_number: "1.144.1"
|
||||||
)
|
)
|
||||||
increment_build_number(
|
increment_build_number(
|
||||||
build_number: latest_testflight_build_number + 1,
|
build_number: latest_testflight_build_number + 1,
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ import 'package:immich_mobile/services/upload.service.dart';
|
|||||||
import 'package:immich_mobile/utils/bootstrap.dart';
|
import 'package:immich_mobile/utils/bootstrap.dart';
|
||||||
import 'package:immich_mobile/utils/debug_print.dart';
|
import 'package:immich_mobile/utils/debug_print.dart';
|
||||||
import 'package:immich_mobile/utils/http_ssl_options.dart';
|
import 'package:immich_mobile/utils/http_ssl_options.dart';
|
||||||
|
import 'package:immich_mobile/wm_executor.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:worker_manager/worker_manager.dart';
|
|
||||||
|
|
||||||
class BackgroundWorkerFgService {
|
class BackgroundWorkerFgService {
|
||||||
final BackgroundWorkerFgHostApi _foregroundHostApi;
|
final BackgroundWorkerFgHostApi _foregroundHostApi;
|
||||||
@@ -93,7 +93,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
|
|||||||
await Future.wait(
|
await Future.wait(
|
||||||
[
|
[
|
||||||
loadTranslations(),
|
loadTranslations(),
|
||||||
workerManager.init(dynamicSpawning: true),
|
workerManagerPatch.init(dynamicSpawning: true),
|
||||||
_ref?.read(authServiceProvider).setOpenApiServiceEndpoint(),
|
_ref?.read(authServiceProvider).setOpenApiServiceEndpoint(),
|
||||||
// Initialize the file downloader
|
// Initialize the file downloader
|
||||||
FileDownloader().configure(
|
FileDownloader().configure(
|
||||||
@@ -198,7 +198,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
|
|||||||
_cancellationToken.cancel();
|
_cancellationToken.cancel();
|
||||||
_logger.info("Cleaning up background worker");
|
_logger.info("Cleaning up background worker");
|
||||||
final cleanupFutures = [
|
final cleanupFutures = [
|
||||||
workerManager.dispose().catchError((_) async {
|
workerManagerPatch.dispose().catchError((_) async {
|
||||||
// Discard any errors on the dispose call
|
// Discard any errors on the dispose call
|
||||||
return;
|
return;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:background_downloader/background_downloader.dart';
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
@@ -38,10 +39,10 @@ import 'package:immich_mobile/utils/debug_print.dart';
|
|||||||
import 'package:immich_mobile/utils/http_ssl_options.dart';
|
import 'package:immich_mobile/utils/http_ssl_options.dart';
|
||||||
import 'package:immich_mobile/utils/licenses.dart';
|
import 'package:immich_mobile/utils/licenses.dart';
|
||||||
import 'package:immich_mobile/utils/migration.dart';
|
import 'package:immich_mobile/utils/migration.dart';
|
||||||
|
import 'package:immich_mobile/wm_executor.dart';
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:timezone/data/latest.dart';
|
import 'package:timezone/data/latest.dart';
|
||||||
import 'package:worker_manager/worker_manager.dart';
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
ImmichWidgetsBinding();
|
ImmichWidgetsBinding();
|
||||||
@@ -50,7 +51,7 @@ void main() async {
|
|||||||
await Bootstrap.initDomain(isar, drift, logDb);
|
await Bootstrap.initDomain(isar, drift, logDb);
|
||||||
await initApp();
|
await initApp();
|
||||||
// Warm-up isolate pool for worker manager
|
// Warm-up isolate pool for worker manager
|
||||||
await workerManager.init(dynamicSpawning: true);
|
await workerManagerPatch.init(dynamicSpawning: true, isolatesCount: max(Platform.numberOfProcessors - 1, 5));
|
||||||
await migrateDatabaseIfNeeded(isar, drift);
|
await migrateDatabaseIfNeeded(isar, drift);
|
||||||
HttpSSLOptions.apply();
|
HttpSSLOptions.apply();
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
|||||||
import 'package:immich_mobile/utils/bootstrap.dart';
|
import 'package:immich_mobile/utils/bootstrap.dart';
|
||||||
import 'package:immich_mobile/utils/debug_print.dart';
|
import 'package:immich_mobile/utils/debug_print.dart';
|
||||||
import 'package:immich_mobile/utils/http_ssl_options.dart';
|
import 'package:immich_mobile/utils/http_ssl_options.dart';
|
||||||
|
import 'package:immich_mobile/wm_executor.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:worker_manager/worker_manager.dart';
|
import 'package:worker_manager/worker_manager.dart';
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ Cancelable<T?> runInIsolateGentle<T>({
|
|||||||
throw const InvalidIsolateUsageException();
|
throw const InvalidIsolateUsageException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return workerManager.executeGentle((cancelledChecker) async {
|
return workerManagerPatch.executeGentle((cancelledChecker) async {
|
||||||
T? result;
|
T? result;
|
||||||
await runZonedGuarded(
|
await runZonedGuarded(
|
||||||
() async {
|
() async {
|
||||||
|
|||||||
@@ -0,0 +1,251 @@
|
|||||||
|
// part of 'package:worker_manager/worker_manager.dart';
|
||||||
|
// ignore_for_file: implementation_imports, avoid_print
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:worker_manager/src/number_of_processors/processors_io.dart';
|
||||||
|
import 'package:worker_manager/src/worker/worker.dart';
|
||||||
|
import 'package:worker_manager/worker_manager.dart';
|
||||||
|
|
||||||
|
final workerManagerPatch = _Executor();
|
||||||
|
|
||||||
|
// [-2^54; 2^53] is compatible with dart2js, see core.int doc
|
||||||
|
const _minId = -9007199254740992;
|
||||||
|
const _maxId = 9007199254740992;
|
||||||
|
|
||||||
|
class Mixinable<T> {
|
||||||
|
late final itSelf = this as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin _ExecutorLogger on Mixinable<_Executor> {
|
||||||
|
var log = false;
|
||||||
|
|
||||||
|
@mustCallSuper
|
||||||
|
void init() {
|
||||||
|
logMessage("${itSelf._isolatesCount} workers have been spawned and initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
void logTaskAdded<R>(String uid) {
|
||||||
|
logMessage("added task with number $uid");
|
||||||
|
}
|
||||||
|
|
||||||
|
@mustCallSuper
|
||||||
|
void dispose() {
|
||||||
|
logMessage("worker_manager have been disposed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@mustCallSuper
|
||||||
|
void _cancel(Task task) {
|
||||||
|
logMessage("Task ${task.id} have been canceled");
|
||||||
|
}
|
||||||
|
|
||||||
|
void logMessage(String message) {
|
||||||
|
if (log) print(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Executor extends Mixinable<_Executor> with _ExecutorLogger {
|
||||||
|
final _queue = PriorityQueue<Task>();
|
||||||
|
final _pool = <Worker>[];
|
||||||
|
var _nextTaskId = _minId;
|
||||||
|
var _dynamicSpawning = false;
|
||||||
|
var _isolatesCount = numberOfProcessors;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> init({int? isolatesCount, bool? dynamicSpawning}) async {
|
||||||
|
if (_pool.isNotEmpty) {
|
||||||
|
print("worker_manager already warmed up, init is ignored. Dispose before init");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isolatesCount != null) {
|
||||||
|
if (isolatesCount < 0) {
|
||||||
|
throw Exception("isolatesCount must be greater than 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
_isolatesCount = isolatesCount;
|
||||||
|
}
|
||||||
|
_dynamicSpawning = dynamicSpawning ?? false;
|
||||||
|
await _ensureWorkersInitialized();
|
||||||
|
super.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> dispose() async {
|
||||||
|
_queue.clear();
|
||||||
|
for (final worker in _pool) {
|
||||||
|
worker.kill();
|
||||||
|
}
|
||||||
|
_pool.clear();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cancelable<R> execute<R>(Execute<R> execution, {WorkPriority priority = WorkPriority.immediately}) {
|
||||||
|
return _createCancelable<R>(execution: execution, priority: priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cancelable<R> executeNow<R>(ExecuteGentle<R> execution) {
|
||||||
|
final task = TaskGentle<R>(
|
||||||
|
id: "",
|
||||||
|
workPriority: WorkPriority.immediately,
|
||||||
|
execution: execution,
|
||||||
|
completer: Completer<R>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<void> run() async {
|
||||||
|
try {
|
||||||
|
final result = await execution(() => task.canceled);
|
||||||
|
task.complete(result, null, null);
|
||||||
|
} catch (error, st) {
|
||||||
|
task.complete(null, error, st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
return Cancelable(completer: task.completer, onCancel: () => _cancel(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
Cancelable<R> executeWithPort<R, T>(
|
||||||
|
ExecuteWithPort<R> execution, {
|
||||||
|
WorkPriority priority = WorkPriority.immediately,
|
||||||
|
required void Function(T value) onMessage,
|
||||||
|
}) {
|
||||||
|
return _createCancelable<R>(
|
||||||
|
execution: execution,
|
||||||
|
priority: priority,
|
||||||
|
onMessage: (message) => onMessage(message as T),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cancelable<R> executeGentle<R>(ExecuteGentle<R> execution, {WorkPriority priority = WorkPriority.immediately}) {
|
||||||
|
return _createCancelable<R>(execution: execution, priority: priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cancelable<R> executeGentleWithPort<R, T>(
|
||||||
|
ExecuteGentleWithPort<R> execution, {
|
||||||
|
WorkPriority priority = WorkPriority.immediately,
|
||||||
|
required void Function(T value) onMessage,
|
||||||
|
}) {
|
||||||
|
return _createCancelable<R>(
|
||||||
|
execution: execution,
|
||||||
|
priority: priority,
|
||||||
|
onMessage: (message) => onMessage(message as T),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _createWorkers() {
|
||||||
|
for (var i = 0; i < _isolatesCount; i++) {
|
||||||
|
_pool.add(Worker());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initializeWorkers() async {
|
||||||
|
await Future.wait(_pool.map((e) => e.initialize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Cancelable<R> _createCancelable<R>({
|
||||||
|
required Function execution,
|
||||||
|
WorkPriority priority = WorkPriority.immediately,
|
||||||
|
void Function(Object value)? onMessage,
|
||||||
|
}) {
|
||||||
|
if (_nextTaskId + 1 == _maxId) {
|
||||||
|
_nextTaskId = _minId;
|
||||||
|
}
|
||||||
|
final id = _nextTaskId.toString();
|
||||||
|
_nextTaskId++;
|
||||||
|
late final Task<R> task;
|
||||||
|
final completer = Completer<R>();
|
||||||
|
if (execution is Execute<R>) {
|
||||||
|
task = TaskRegular<R>(id: id, workPriority: priority, execution: execution, completer: completer);
|
||||||
|
} else if (execution is ExecuteWithPort<R>) {
|
||||||
|
task = TaskWithPort<R>(
|
||||||
|
id: id,
|
||||||
|
workPriority: priority,
|
||||||
|
execution: execution,
|
||||||
|
completer: completer,
|
||||||
|
onMessage: onMessage!,
|
||||||
|
);
|
||||||
|
} else if (execution is ExecuteGentle<R>) {
|
||||||
|
task = TaskGentle<R>(id: id, workPriority: priority, execution: execution, completer: completer);
|
||||||
|
} else if (execution is ExecuteGentleWithPort<R>) {
|
||||||
|
task = TaskGentleWithPort<R>(
|
||||||
|
id: id,
|
||||||
|
workPriority: priority,
|
||||||
|
execution: execution,
|
||||||
|
completer: completer,
|
||||||
|
onMessage: onMessage!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_queue.add(task);
|
||||||
|
_schedule();
|
||||||
|
logTaskAdded(task.id);
|
||||||
|
return Cancelable(completer: task.completer, onCancel: () => _cancel(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _ensureWorkersInitialized() async {
|
||||||
|
if (_pool.isEmpty) {
|
||||||
|
_createWorkers();
|
||||||
|
if (!_dynamicSpawning) {
|
||||||
|
await _initializeWorkers();
|
||||||
|
final poolSize = _pool.length;
|
||||||
|
final queueSize = _queue.length;
|
||||||
|
for (int i = 0; i <= min(poolSize, queueSize); i++) {
|
||||||
|
_schedule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_pool.every((worker) => worker.taskId != null)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_dynamicSpawning) {
|
||||||
|
final freeWorker = _pool.firstWhereOrNull(
|
||||||
|
(worker) => worker.taskId == null && !worker.initialized && !worker.initializing,
|
||||||
|
);
|
||||||
|
await freeWorker?.initialize();
|
||||||
|
_schedule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _schedule() {
|
||||||
|
final availableWorker = _pool.firstWhereOrNull((worker) => worker.taskId == null && worker.initialized);
|
||||||
|
if (availableWorker == null) {
|
||||||
|
_ensureWorkersInitialized();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_queue.isEmpty) return;
|
||||||
|
final task = _queue.removeFirst();
|
||||||
|
|
||||||
|
availableWorker
|
||||||
|
.work(task)
|
||||||
|
.then(
|
||||||
|
(value) {
|
||||||
|
//could be completed already by cancel and it is normal.
|
||||||
|
//Assuming that worker finished with error and cleaned gracefully
|
||||||
|
task.complete(value, null, null);
|
||||||
|
},
|
||||||
|
onError: (error, st) {
|
||||||
|
task.complete(null, error, st);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.whenComplete(() {
|
||||||
|
if (_dynamicSpawning && _queue.isEmpty) availableWorker.kill();
|
||||||
|
_schedule();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void _cancel(Task task) {
|
||||||
|
task.cancel();
|
||||||
|
_queue.remove(task);
|
||||||
|
final targetWorker = _pool.firstWhereOrNull((worker) => worker.taskId == task.id);
|
||||||
|
if (task is Gentle) {
|
||||||
|
targetWorker?.cancelGentle();
|
||||||
|
} else {
|
||||||
|
targetWorker?.kill();
|
||||||
|
if (!_dynamicSpawning) targetWorker?.initialize();
|
||||||
|
}
|
||||||
|
super._cancel(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+1
-1
@@ -3,7 +3,7 @@ Immich API
|
|||||||
|
|
||||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||||
|
|
||||||
- API version: 2.0.0
|
- API version: 1.144.1
|
||||||
- Generator version: 7.8.0
|
- Generator version: 7.8.0
|
||||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ name: immich_mobile
|
|||||||
description: Immich - selfhosted backup media file on mobile phone
|
description: Immich - selfhosted backup media file on mobile phone
|
||||||
|
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 2.0.0+3020
|
version: 1.144.1+3019
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.8.0 <4.0.0'
|
sdk: '>=3.8.0 <4.0.0'
|
||||||
|
|||||||
@@ -9858,7 +9858,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Immich",
|
"title": "Immich",
|
||||||
"description": "Immich API",
|
"description": "Immich API",
|
||||||
"version": "2.0.0",
|
"version": "1.144.1",
|
||||||
"contact": {}
|
"contact": {}
|
||||||
},
|
},
|
||||||
"tags": [],
|
"tags": [],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "2.0.0",
|
"version": "1.144.1",
|
||||||
"description": "Auto-generated TypeScript SDK for the Immich API",
|
"description": "Auto-generated TypeScript SDK for the Immich API",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./build/index.js",
|
"main": "./build/index.js",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Immich
|
* Immich
|
||||||
* 2.0.0
|
* 1.144.1
|
||||||
* DO NOT MODIFY - This file has been generated using oazapfts.
|
* DO NOT MODIFY - This file has been generated using oazapfts.
|
||||||
* See https://www.npmjs.com/package/oazapfts
|
* See https://www.npmjs.com/package/oazapfts
|
||||||
*/
|
*/
|
||||||
|
|||||||
+8
-23
@@ -1,50 +1,35 @@
|
|||||||
FROM ghcr.io/immich-app/base-server-dev:202509210934@sha256:b5ce2d7eaf379d4cf15efd4bab180d8afc8a80d20b36c9800f4091aca6ae267e AS builder
|
FROM ghcr.io/immich-app/base-server-dev:202509210934@sha256:b5ce2d7eaf379d4cf15efd4bab180d8afc8a80d20b36c9800f4091aca6ae267e AS builder
|
||||||
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||||
CI=1 \
|
CI=1 \
|
||||||
COREPACK_HOME=/tmp \
|
COREPACK_HOME=/tmp
|
||||||
PNPM_HOME=/pnpm \
|
|
||||||
PATH="/pnpm:$PATH"
|
|
||||||
|
|
||||||
RUN npm install --global corepack@latest && \
|
RUN npm install --global corepack@latest && \
|
||||||
corepack enable pnpm && \
|
corepack enable pnpm
|
||||||
pnpm config set store-dir "$PNPM_HOME"
|
|
||||||
|
|
||||||
FROM builder AS server
|
FROM builder AS server
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
COPY ./package* ./pnpm* .pnpmfile.cjs ./
|
||||||
COPY ./server ./server/
|
COPY ./server ./server/
|
||||||
RUN --mount=type=cache,id=pnpm-server,target=/pnpm \
|
RUN SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \
|
||||||
--mount=type=bind,source=package.json,target=package.json \
|
|
||||||
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
|
||||||
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
|
||||||
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
|
||||||
SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \
|
|
||||||
SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned
|
SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned
|
||||||
|
|
||||||
FROM builder AS web
|
FROM builder AS web
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
COPY ./package* ./pnpm* .pnpmfile.cjs ./
|
||||||
COPY ./web ./web/
|
COPY ./web ./web/
|
||||||
COPY ./i18n ./i18n/
|
COPY ./i18n ./i18n/
|
||||||
COPY ./open-api ./open-api/
|
COPY ./open-api ./open-api/
|
||||||
RUN --mount=type=cache,id=pnpm-web,target=/pnpm \
|
RUN SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install && \
|
||||||
--mount=type=bind,source=package.json,target=package.json \
|
|
||||||
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
|
||||||
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
|
||||||
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
|
||||||
SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install && \
|
|
||||||
pnpm --filter @immich/sdk --filter immich-web build
|
pnpm --filter @immich/sdk --filter immich-web build
|
||||||
|
|
||||||
FROM builder AS cli
|
FROM builder AS cli
|
||||||
|
|
||||||
|
COPY ./package* ./pnpm* .pnpmfile.cjs ./
|
||||||
COPY ./cli ./cli/
|
COPY ./cli ./cli/
|
||||||
COPY ./open-api ./open-api/
|
COPY ./open-api ./open-api/
|
||||||
RUN --mount=type=cache,id=pnpm-cli,target=/pnpm \
|
RUN pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && \
|
||||||
--mount=type=bind,source=package.json,target=package.json \
|
|
||||||
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
|
||||||
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
|
||||||
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
|
||||||
pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && \
|
|
||||||
pnpm --filter @immich/sdk --filter @immich/cli build && \
|
pnpm --filter @immich/sdk --filter @immich/cli build && \
|
||||||
pnpm --filter @immich/cli --prod --no-optional deploy /output/cli-pruned
|
pnpm --filter @immich/cli --prod --no-optional deploy /output/cli-pruned
|
||||||
|
|
||||||
|
|||||||
+6
-7
@@ -1,13 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
echo "Initializing Immich $IMMICH_SOURCE_REF"
|
echo "Initializing Immich $IMMICH_SOURCE_REF"
|
||||||
|
|
||||||
# TODO: Update to mimalloc v3 when verified memory isn't released issue is fixed
|
lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.3"
|
||||||
# lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.3"
|
if [ -f "$lib_path" ]; then
|
||||||
# if [ -f "$lib_path" ]; then
|
export LD_PRELOAD="$lib_path"
|
||||||
# export LD_PRELOAD="$lib_path"
|
else
|
||||||
# else
|
echo "skipping libmimalloc - path not found $lib_path"
|
||||||
# echo "skipping libmimalloc - path not found $lib_path"
|
fi
|
||||||
# fi
|
|
||||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib"
|
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib"
|
||||||
SERVER_HOME="$(readlink -f "$(dirname "$0")/..")"
|
SERVER_HOME="$(readlink -f "$(dirname "$0")/..")"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "2.0.0",
|
"version": "1.144.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ export const AlbumUpdateEmail = ({
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text>
|
<Text>
|
||||||
New media has been added to <strong>{albumName}</strong>.
|
New media has been added to <strong>{albumName}</strong>,
|
||||||
<br /> Check it out!
|
<br /> check it out!
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Button, ButtonProps, Text } from '@react-email/components';
|
import { Button, ButtonProps } from '@react-email/components';
|
||||||
|
|
||||||
export const ImmichButton = ({ children, ...props }: ButtonProps) => (
|
export const ImmichButton = ({ children, ...props }: ButtonProps) => (
|
||||||
<Button
|
<Button
|
||||||
{...props}
|
{...props}
|
||||||
className="border bg-immich-primary rounded-full no-underline hover:no-underline text-white hover:text-gray-50 font-bold uppercase"
|
className="py-3 px-8 border bg-immich-primary rounded-full no-underline hover:no-underline text-white hover:text-gray-50 font-bold uppercase"
|
||||||
>
|
>
|
||||||
<Text className="my-3 mx-8">{children}</Text>
|
{children}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-web",
|
"name": "immich-web",
|
||||||
"version": "2.0.0",
|
"version": "1.144.1",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user