Compare commits
4 Commits
tmp_photos
...
chore/devc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61689258be | ||
|
|
68f3ed89c5 | ||
|
|
78516a97b3 | ||
|
|
b8a17c3c26 |
@@ -5,8 +5,7 @@
|
|||||||
"immich-server",
|
"immich-server",
|
||||||
"redis",
|
"redis",
|
||||||
"database",
|
"database",
|
||||||
"immich-machine-learning",
|
"immich-machine-learning"
|
||||||
"init"
|
|
||||||
],
|
],
|
||||||
"dockerComposeFile": [
|
"dockerComposeFile": [
|
||||||
"../docker/docker-compose.dev.yml",
|
"../docker/docker-compose.dev.yml",
|
||||||
|
|||||||
@@ -6,28 +6,35 @@ services:
|
|||||||
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
||||||
volumes: !override # bind mount host to /workspaces/immich
|
volumes: !override # bind mount host to /workspaces/immich
|
||||||
- ..:/workspaces/immich
|
- ..:/workspaces/immich
|
||||||
- cli_node_modules:/workspaces/immich/cli/node_modules
|
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
|
||||||
- e2e_node_modules:/workspaces/immich/e2e/node_modules
|
- pnpm-store:/usr/src/app/.pnpm-store
|
||||||
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules
|
- server-node_modules:/usr/src/app/server/node_modules
|
||||||
- server_node_modules:/workspaces/immich/server/node_modules
|
- web-node_modules:/usr/src/app/web/node_modules
|
||||||
- web_node_modules:/workspaces/immich/web/node_modules
|
- github-node_modules:/usr/src/app/.github/node_modules
|
||||||
- ${UPLOAD_LOCATION}/photos:/data
|
- cli-node_modules:/usr/src/app/cli/node_modules
|
||||||
|
- docs-node_modules:/usr/src/app/docs/node_modules
|
||||||
|
- e2e-node_modules:/usr/src/app/e2e/node_modules
|
||||||
|
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
|
||||||
|
- app-node_modules:/usr/src/app/node_modules
|
||||||
|
- sveltekit:/usr/src/app/web/.svelte-kit
|
||||||
|
- coverage:/usr/src/app/web/coverage
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
immich-web:
|
||||||
|
env_file: !reset []
|
||||||
|
immich-machine-learning:
|
||||||
|
env_file: !reset []
|
||||||
database:
|
database:
|
||||||
|
env_file: !reset []
|
||||||
|
environment: !override
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD-postgres}
|
||||||
|
POSTGRES_USER: ${DB_USERNAME-postgres}
|
||||||
|
POSTGRES_DB: ${DB_DATABASE_NAME-immich}
|
||||||
|
POSTGRES_INITDB_ARGS: '--data-checksums'
|
||||||
|
POSTGRES_HOST_AUTH_METHOD: md5
|
||||||
volumes:
|
volumes:
|
||||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
- ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data
|
||||||
|
redis:
|
||||||
|
env_file: !reset []
|
||||||
volumes:
|
volumes:
|
||||||
# Node modules for each service to avoid conflicts and ensure consistent dependencies
|
upload-devcontainer-volume:
|
||||||
cli_node_modules:
|
postgres-devcontainer-volume:
|
||||||
e2e_node_modules:
|
|
||||||
open_api_node_modules:
|
|
||||||
server_node_modules:
|
|
||||||
web_node_modules:
|
|
||||||
|
|
||||||
# UPLOAD_LOCATION must be set to a absolute path or vol-upload
|
|
||||||
vol-upload:
|
|
||||||
|
|
||||||
# DB_DATA_LOCATION must be set to a absolute path or vol-database
|
|
||||||
vol-database:
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
"userEnvProbe": "loginInteractiveShell",
|
"userEnvProbe": "loginInteractiveShell",
|
||||||
"remoteEnv": {
|
"remoteEnv": {
|
||||||
// The location where your uploaded files are stored
|
// The location where your uploaded files are stored
|
||||||
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}",
|
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./library}",
|
||||||
// Connection secret for postgres. You should change it to a random password
|
// Connection secret for postgres. You should change it to a random password
|
||||||
// Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
// Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||||
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
||||||
|
|||||||
@@ -322,6 +322,8 @@ class ScrubberState extends ConsumerState<Scrubber> with TickerProviderStateMixi
|
|||||||
_isDragging = false;
|
_isDragging = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ref.read(timelineStateProvider.notifier).setScrubbing(false);
|
||||||
|
|
||||||
// Reset scrubber tracking when drag ends
|
// Reset scrubber tracking when drag ends
|
||||||
_currentScrubberDate = null;
|
_currentScrubberDate = null;
|
||||||
_scrubberDebouncer?.dispose();
|
_scrubberDebouncer?.dispose();
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ ENTRYPOINT ["tini", "--", "/bin/bash", "-c"]
|
|||||||
FROM dev AS dev-container-server
|
FROM dev AS dev-container-server
|
||||||
|
|
||||||
RUN apt-get update --allow-releaseinfo-change && \
|
RUN apt-get update --allow-releaseinfo-change && \
|
||||||
apt-get install sudo inetutils-ping openjdk-11-jre-headless \
|
apt-get install sudo inetutils-ping openjdk-21-jre-headless \
|
||||||
vim nano \
|
vim nano curl \
|
||||||
-y --no-install-recommends --fix-missing
|
-y --no-install-recommends --fix-missing
|
||||||
|
|
||||||
RUN usermod -aG sudo node && \
|
RUN usermod -aG sudo node && \
|
||||||
@@ -44,13 +44,18 @@ FROM dev-container-server AS dev-container-mobile
|
|||||||
USER root
|
USER root
|
||||||
# Enable multiarch for arm64 if necessary
|
# Enable multiarch for arm64 if necessary
|
||||||
RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \
|
RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \
|
||||||
dpkg --add-architecture amd64 && \
|
dpkg --add-architecture amd64 && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
qemu-user-static \
|
gnupg \
|
||||||
libc6:amd64 \
|
qemu-user-static \
|
||||||
libstdc++6:amd64 \
|
libc6:amd64 \
|
||||||
libgcc1:amd64; \
|
libstdc++6:amd64 \
|
||||||
|
libgcc1:amd64; \
|
||||||
|
else \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
gnupg; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Flutter SDK
|
# Flutter SDK
|
||||||
@@ -65,11 +70,11 @@ RUN mkdir -p ${FLUTTER_HOME} \
|
|||||||
&& curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \
|
&& curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \
|
||||||
&& tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \
|
&& tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \
|
||||||
&& rm flutter.tar.xz \
|
&& rm flutter.tar.xz \
|
||||||
&& chown -R node ${FLUTTER_HOME}
|
&& chown -R node ${FLUTTER_HOME} \
|
||||||
|
&& git config --global --add safe.directory ${FLUTTER_HOME}
|
||||||
|
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \
|
||||||
&& wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \
|
|
||||||
&& echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | tee /etc/apt/sources.list.d/dart_stable.list \
|
&& echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | tee /etc/apt/sources.list.d/dart_stable.list \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install dcm -y
|
&& apt-get install dcm -y
|
||||||
|
|||||||
@@ -57,28 +57,28 @@ export class MediaRepository {
|
|||||||
const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw2', input);
|
const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw2', input);
|
||||||
return { buffer, format: RawExtractedFormat.Jpeg };
|
return { buffer, format: RawExtractedFormat.Jpeg };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.debug('Could not extract JpgFromRaw2 buffer from image, trying JPEG from RAW next', error.message);
|
this.logger.debug(`Could not extract JpgFromRaw2 buffer from image, trying JPEG from RAW next: ${error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw', input);
|
const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw', input);
|
||||||
return { buffer, format: RawExtractedFormat.Jpeg };
|
return { buffer, format: RawExtractedFormat.Jpeg };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.debug('Could not extract JPEG buffer from image, trying PreviewJXL next', error.message);
|
this.logger.debug(`Could not extract JPEG buffer from image, trying PreviewJXL next: ${error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const buffer = await exiftool.extractBinaryTagToBuffer('PreviewJXL', input);
|
const buffer = await exiftool.extractBinaryTagToBuffer('PreviewJXL', input);
|
||||||
return { buffer, format: RawExtractedFormat.Jxl };
|
return { buffer, format: RawExtractedFormat.Jxl };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.debug('Could not extract PreviewJXL buffer from image, trying PreviewImage next', error.message);
|
this.logger.debug(`Could not extract PreviewJXL buffer from image, trying PreviewImage next: ${error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const buffer = await exiftool.extractBinaryTagToBuffer('PreviewImage', input);
|
const buffer = await exiftool.extractBinaryTagToBuffer('PreviewImage', input);
|
||||||
return { buffer, format: RawExtractedFormat.Jpeg };
|
return { buffer, format: RawExtractedFormat.Jpeg };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.debug('Could not extract preview buffer from image', error.message);
|
this.logger.debug(`Could not extract preview buffer from image: ${error}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export class MetadataRepository {
|
|||||||
|
|
||||||
readTags(path: string): Promise<ImmichTags> {
|
readTags(path: string): Promise<ImmichTags> {
|
||||||
return this.exiftool.read(path).catch((error) => {
|
return this.exiftool.read(path).catch((error) => {
|
||||||
this.logger.warn(`Error reading exif data (${path}): ${error}`, error?.stack);
|
this.logger.warn(`Error reading exif data (${path}): ${error}\n${error?.stack}`);
|
||||||
return {};
|
return {};
|
||||||
}) as Promise<ImmichTags>;
|
}) as Promise<ImmichTags>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ export class AuthService extends BaseService {
|
|||||||
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [oldPath] } });
|
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [oldPath] } });
|
||||||
}
|
}
|
||||||
} catch (error: Error | any) {
|
} catch (error: Error | any) {
|
||||||
this.logger.warn(`Unable to sync oauth profile picture: ${error}`, error?.stack);
|
this.logger.warn(`Unable to sync oauth profile picture: ${error}\n${error?.stack}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,12 +132,12 @@ export class BackupService extends BaseService {
|
|||||||
gzip.stdout.pipe(fileStream);
|
gzip.stdout.pipe(fileStream);
|
||||||
|
|
||||||
pgdump.on('error', (err) => {
|
pgdump.on('error', (err) => {
|
||||||
this.logger.error('Backup failed with error', err);
|
this.logger.error(`Backup failed with error: ${err}`);
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
gzip.on('error', (err) => {
|
gzip.on('error', (err) => {
|
||||||
this.logger.error('Gzip failed with error', err);
|
this.logger.error(`Gzip failed with error: ${err}`);
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -175,10 +175,10 @@ export class BackupService extends BaseService {
|
|||||||
});
|
});
|
||||||
await this.storageRepository.rename(backupFilePath, backupFilePath.replace('.tmp', ''));
|
await this.storageRepository.rename(backupFilePath, backupFilePath.replace('.tmp', ''));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Database Backup Failure', error);
|
this.logger.error(`Database Backup Failure: ${error}`);
|
||||||
await this.storageRepository
|
await this.storageRepository
|
||||||
.unlink(backupFilePath)
|
.unlink(backupFilePath)
|
||||||
.catch((error) => this.logger.error('Failed to delete failed backup file', error));
|
.catch((error) => this.logger.error(`Failed to delete failed backup file: ${error}`));
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ export class LibraryService extends BaseService {
|
|||||||
job.paths.map((path) =>
|
job.paths.map((path) =>
|
||||||
this.processEntity(path, library.ownerId, job.libraryId)
|
this.processEntity(path, library.ownerId, job.libraryId)
|
||||||
.then((asset) => assetImports.push(asset))
|
.then((asset) => assetImports.push(asset))
|
||||||
.catch((error: any) => this.logger.error(`Error processing ${path} for library ${job.libraryId}`, error)),
|
.catch((error: any) => this.logger.error(`Error processing ${path} for library ${job.libraryId}: ${error}`)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class MemoryService extends BaseService {
|
|||||||
try {
|
try {
|
||||||
await Promise.all(users.map((owner, i) => this.createOnThisDayMemories(owner.id, usersIds[i], target)));
|
await Promise.all(users.map((owner, i) => this.createOnThisDayMemories(owner.id, usersIds[i], target)));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Failed to create memories for ${target.toISO()}`, error);
|
this.logger.error(`Failed to create memories for ${target.toISO()}: ${error}`);
|
||||||
}
|
}
|
||||||
// update system metadata even when there is an error to minimize the chance of duplicates
|
// update system metadata even when there is an error to minimize the chance of duplicates
|
||||||
await this.systemMetadataRepository.set(SystemMetadataKey.MemoriesState, {
|
await this.systemMetadataRepository.set(SystemMetadataKey.MemoriesState, {
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ export class StorageTemplateService extends BaseService {
|
|||||||
|
|
||||||
return destination;
|
return destination;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error(`Unable to get template path for ${filename}`, error);
|
this.logger.error(`Unable to get template path for ${filename}: ${error}`);
|
||||||
return asset.originalPath;
|
return asset.originalPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export class VersionService extends BaseService {
|
|||||||
this.eventRepository.clientBroadcast('on_new_release', asNotification(metadata));
|
this.eventRepository.clientBroadcast('on_new_release', asNotification(metadata));
|
||||||
}
|
}
|
||||||
} catch (error: Error | any) {
|
} catch (error: Error | any) {
|
||||||
this.logger.warn(`Unable to run version check: ${error}`, error?.stack);
|
this.logger.warn(`Unable to run version check: ${error}\n${error?.stack}`);
|
||||||
return JobStatus.Failed;
|
return JobStatus.Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export const sendFile = async (
|
|||||||
|
|
||||||
// log non-http errors
|
// log non-http errors
|
||||||
if (error instanceof HttpException === false) {
|
if (error instanceof HttpException === false) {
|
||||||
logger.error(`Unable to send file: ${error.name}`, error.stack);
|
logger.error(`Unable to send file: ${error}`, error.stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.header('Cache-Control', 'none');
|
res.header('Cache-Control', 'none');
|
||||||
|
|||||||
Reference in New Issue
Block a user