Compare commits
99 Commits
v1.3.0-dev
...
v1.12.0_18
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7603fd150 | ||
|
|
517a3363d6 | ||
|
|
3511b69fc8 | ||
|
|
e6efc61b3b | ||
|
|
360c1d9a15 | ||
|
|
e3449f9c8f | ||
|
|
a8e723d722 | ||
|
|
d116234523 | ||
|
|
dce2bc7508 | ||
|
|
2bf764f560 | ||
|
|
587b77e70b | ||
|
|
53cd9fd8bf | ||
|
|
a8220172f8 | ||
|
|
397f8c70b4 | ||
|
|
68ff5377b0 | ||
|
|
b359dc3cb6 | ||
|
|
5b036067ed | ||
|
|
b9f38162d5 | ||
|
|
ab6909bfbd | ||
|
|
53c3c916a6 | ||
|
|
6924aa5eb1 | ||
|
|
a3b45d62b6 | ||
|
|
b34de624ce | ||
|
|
7886c42742 | ||
|
|
d476b15312 | ||
|
|
bdf38e7668 | ||
|
|
e33566a04a | ||
|
|
c28251b8b4 | ||
|
|
337db1c508 | ||
|
|
ad2a1ba901 | ||
|
|
fa6f6f8e9f | ||
|
|
a44043a4e5 | ||
|
|
87b15c60c0 | ||
|
|
2c83e52c15 | ||
|
|
55c5027539 | ||
|
|
ce06af0c9b | ||
|
|
baaf7ad153 | ||
|
|
4a25e7dc22 | ||
|
|
f90563d18c | ||
|
|
8352ecd3b9 | ||
|
|
69b34a4364 | ||
|
|
6023c3c624 | ||
|
|
171e7ffa77 | ||
|
|
d9f918005a | ||
|
|
e8ade4866b | ||
|
|
bbfa789a4e | ||
|
|
a779c3803c | ||
|
|
79dea504b0 | ||
|
|
4900fecd10 | ||
|
|
adfaab7eb2 | ||
|
|
c5adbea6e1 | ||
|
|
bb89fa4aab | ||
|
|
43d639104d | ||
|
|
a1792a7d94 | ||
|
|
373b6918f8 | ||
|
|
f1396761b0 | ||
|
|
335bb0707c | ||
|
|
7a51e0dd4d | ||
|
|
5b42899dde | ||
|
|
229357df2b | ||
|
|
8d5626620b | ||
|
|
2c4243b3d0 | ||
|
|
38e0178c81 | ||
|
|
c5c7a134dd | ||
|
|
da9eb61532 | ||
|
|
c1ccf026f0 | ||
|
|
4309104925 | ||
|
|
a3b84b3ca7 | ||
|
|
f6630163b1 | ||
|
|
aebeb37fb0 | ||
|
|
b74ad69288 | ||
|
|
b6579cd38e | ||
|
|
46a2032b9a | ||
|
|
0eb548f115 | ||
|
|
c7dff229db | ||
|
|
8e80825b4f | ||
|
|
a1481c1113 | ||
|
|
3bdcdef198 | ||
|
|
b69f6e0df7 | ||
|
|
be2794a372 | ||
|
|
2ff25b49f4 | ||
|
|
135d72d4cd | ||
|
|
90ef64efa3 | ||
|
|
60df387459 | ||
|
|
fc1acf6f01 | ||
|
|
cfc5229964 | ||
|
|
f9ddeac265 | ||
|
|
8d7c576037 | ||
|
|
fccdbdd66a | ||
|
|
23ba651705 | ||
|
|
ac0ad98b55 | ||
|
|
9cbd5d1b0c | ||
|
|
80fd664cc8 | ||
|
|
041c711cb9 | ||
|
|
dd9c5244fd | ||
|
|
fe693db84f | ||
|
|
5c9d3cd08b | ||
|
|
725ab5622f | ||
|
|
e9acd21733 |
1
.github/FUNDING.yml
vendored
@@ -1,3 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: alextran1502
|
||||
custom: https://www.buymeacoffee.com/altran1502?new=1
|
||||
15
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,15 +1,26 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
title: '[BUG] <title>'
|
||||
labels: bug, need triage
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Note: Please search to see if an issue already exists for the bug you encountered.
|
||||
-->
|
||||
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Task List**
|
||||
- [ ] I have read thoroughly the README setup and installation instructions.
|
||||
- [ ] If my setup is different, I have included my docker-compose file.
|
||||
- [ ] I have included my redacted `.env` file.
|
||||
- [ ] I have included information on my machine, and environment.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
32
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Feature Request
|
||||
description: Request a feature that you would like for the app
|
||||
title: "[Feature]: "
|
||||
labels: ["feature", "need triage"]
|
||||
assignees:
|
||||
- ""
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this feature request!
|
||||
|
||||
- type: textarea
|
||||
id: feature-detail
|
||||
attributes:
|
||||
label: Feature detail
|
||||
placeholder: Describe the feature you would like to see for the app
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Choose the platform for the feature
|
||||
options:
|
||||
- Web
|
||||
- Mobile App
|
||||
- Server
|
||||
validations:
|
||||
required: true
|
||||
93
.github/workflows/build_push_docker_latest.yml
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
name: Build and Push Docker Image - Latest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
# This image include both the server and microservices - the two containers can be slitted into separated
|
||||
# service with its coressponding entry file.
|
||||
build_and_push_server_monorepo_latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push Immich Mono Repo
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
context: ./server
|
||||
file: ./server/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
altran1502/immich-server:latest
|
||||
|
||||
build_and_push_machine_learning_latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Machine Learning
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
context: ./machine-learning
|
||||
file: ./machine-learning/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64
|
||||
push: true
|
||||
tags: |
|
||||
altran1502/immich-machine-learning:latest
|
||||
|
||||
build_and_push_web_latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Web
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
context: ./web
|
||||
file: ./web/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
target: prod
|
||||
push: true
|
||||
tags: |
|
||||
altran1502/immich-web:latest
|
||||
95
.github/workflows/build_push_docker_staging.yml
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
name: Build and Push Docker Image - Staging
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
# This image include both the server and microservices - the two containers can be slitted into separated
|
||||
# service with its coressponding entry file.
|
||||
build_and_push_server_monorepo_staging:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push Immich Mono Repo
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
context: ./server
|
||||
file: ./server/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name == 'pull_request' }}
|
||||
tags: |
|
||||
altran1502/immich-server:staging
|
||||
|
||||
build_and_push_machine_learning_staging:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Machine Learning
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
context: ./machine-learning
|
||||
file: ./machine-learning/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64
|
||||
push: ${{ github.event_name == 'pull_request' }}
|
||||
tags: |
|
||||
altran1502/immich-machine-learning:staging
|
||||
|
||||
build_and_push_web_staging:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Web
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
context: ./web
|
||||
file: ./web/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
target: prod
|
||||
push: ${{ github.event_name == 'pull_request' }}
|
||||
tags: |
|
||||
altran1502/immich-web:staging
|
||||
46
.github/workflows/build_push_server.yml
vendored
@@ -1,46 +0,0 @@
|
||||
name: Build Server
|
||||
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the main branch
|
||||
#schedule:
|
||||
# * is a special character in YAML so you have to quote this string
|
||||
#- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
buildandpush:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: "main" # branch
|
||||
# https://github.com/docker/setup-qemu-action#usage
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1.2.0
|
||||
# https://github.com/marketplace/actions/docker-setup-buildx
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1.6.0
|
||||
# https://github.com/docker/login-action#docker-hub
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
# https://github.com/docker/build-push-action#multi-platform-image
|
||||
- name: Build and push Immich
|
||||
uses: docker/build-push-action@v2.10.0
|
||||
with:
|
||||
context: ./server
|
||||
file: ./server/Dockerfile
|
||||
#platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
pull: true
|
||||
push: true
|
||||
tags: |
|
||||
altran1502/immich-server:latest
|
||||
116
.github/workflows/build_push_server_release.yml
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
name: Build and push Docker image - Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build_and_push_server_monorepo_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: "main"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Get Previous tag"
|
||||
id: previoustag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: latest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push immich-server release
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
context: ./server
|
||||
file: ./server/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: |
|
||||
altran1502/immich-server:${{ steps.previoustag.outputs.tag }}
|
||||
|
||||
build_and_push_machine_learning_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: "Get Previous tag"
|
||||
id: previoustag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: latest
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and Push Machine Learning
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
context: ./machine-learning
|
||||
file: ./machine-learning/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64
|
||||
push: true
|
||||
tags: |
|
||||
altran1502/immich-machine-learning:${{ steps.previoustag.outputs.tag }}
|
||||
|
||||
build_and_push_web_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: "main"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Get Previous tag"
|
||||
id: previoustag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: latest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push immich-web release
|
||||
uses: docker/build-push-action@v3.0.0
|
||||
with:
|
||||
context: ./web
|
||||
file: ./web/Dockerfile
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
target: prod
|
||||
tags: |
|
||||
altran1502/immich-web:${{ steps.previoustag.outputs.tag }}
|
||||
4
.gitignore
vendored
@@ -1 +1,3 @@
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
.vscode
|
||||
.idea
|
||||
18
Makefile
@@ -1,8 +1,20 @@
|
||||
dev:
|
||||
docker-compose -f ./docker/docker-compose.yml up
|
||||
docker-compose -f ./docker/docker-compose.dev.yml up --remove-orphans
|
||||
|
||||
dev-update:
|
||||
docker-compose -f ./docker/docker-compose.yml up --build -V
|
||||
docker-compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
|
||||
|
||||
dev-scale:
|
||||
docker-compose -f ./docker/docker-compose.yml up --build -V --scale immich_server=3 --remove-orphans
|
||||
docker-compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
|
||||
|
||||
stage:
|
||||
docker-compose -f ./docker/docker-compose.staging.yml up --build -V --remove-orphans
|
||||
|
||||
test-e2e:
|
||||
docker-compose -f ./docker/docker-compose.test.yml --env-file ./docker/.env.test up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich_server_test --remove-orphans
|
||||
|
||||
prod:
|
||||
docker-compose -f ./docker/docker-compose.yml up --build -V --remove-orphans
|
||||
|
||||
prod-scale:
|
||||
docker-compose -f ./docker/docker-compose.yml up --build -V --scale immich-server=5 --scale immich-microservices=3 --remove-orphans
|
||||
9
NOTES.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# TODO
|
||||
|
||||
Server scenario with web
|
||||
|
||||
[ ] 1 user exist without admin right -> make admin on first check
|
||||
|
||||
[ ] 2 users exist without admin right -> ask user to choose which account will be the admin
|
||||
|
||||
[ X ] No users exist -> prompt signup form for Admin
|
||||
@@ -1,13 +1,17 @@
|
||||
# Deployment checklist for iOS/Android/Server
|
||||
|
||||
[] Up version in [mobile/pubspec.yml](/mobile/pubspec.yaml)
|
||||
[ ] Up version in [mobile/pubspec.yml](/mobile/pubspec.yaml)
|
||||
|
||||
[] Up version in [docker/docker-compose.yml](/docker/docker-compose.yml) for `immich_server` service
|
||||
[ ] Up version in [docker/docker-compose.yml](/docker/docker-compose.yml) for `immich_server` service
|
||||
|
||||
[] Up version in [docker/docker-compose.gpu.yml](/docker/docker-compose.gpu.yml) for `immich_server` service
|
||||
[ ] Up version in [docker/docker-compose.gpu.yml](/docker/docker-compose.gpu.yml) for `immich_server` service
|
||||
|
||||
[] Up version in [server/src/constants/server_version.constant.ts](/server/src/constants/server_version.constant.ts)
|
||||
[ ] Up version in [docker/docker-compose.dev.yml](/docker/docker-compose.dev.yml) for `immich_server` service
|
||||
|
||||
[] Up version in iOS Fastlane [/mobile/ios/fastlane/Fastfile](/mobile/ios/fastlane/Fastfile)
|
||||
[ ] Up version in [server/src/constants/server_version.constant.ts](/server/src/constants/server_version.constant.ts)
|
||||
|
||||
[ ] Up version in iOS Fastlane [/mobile/ios/fastlane/Fastfile](/mobile/ios/fastlane/Fastfile)
|
||||
|
||||
[ ] Add changelog to [Android Fastlane F-droid folder](/mobile/android/fastlane/metadata/android/en-US/changelogs)
|
||||
|
||||
All of the version should be the same.
|
||||
224
README.md
@@ -10,14 +10,16 @@
|
||||
<a href="https://actions-badge.atrox.dev/alextran1502/immich/goto?ref=main">
|
||||
<img alt="Build Status" src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Falextran1502%2Fimmich%2Fbadge%3Fref%3Dmain&style=for-the-badge&label=Server Docker&logo=docker&labelColor=ececec" />
|
||||
</a>
|
||||
|
||||
<a href="https://discord.gg/rxnyVTXGbM">
|
||||
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Immich%20Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Immich Discord"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<p align="center">
|
||||
<img src="design/immich-logo.svg" width="200" title="Immich Logo">
|
||||
<img src="design/feature-panel.png" title="Immich Logo">
|
||||
</p>
|
||||
</p>
|
||||
|
||||
@@ -31,47 +33,73 @@ Loading ~4000 images/videos
|
||||
|
||||
## Screenshots
|
||||
|
||||
### Mobile client
|
||||
<p align="left">
|
||||
<img src="design/nsc1.png" width="150" title="Login With Custom URL">
|
||||
<img src="design/nsc2.png" width="150" title="Backup Setting Info">
|
||||
<img src="design/nsc3.png" width="150" title="Multiple seelct">
|
||||
<img src="design/login-screen.png" width="150" title="Login With Custom URL">
|
||||
<img src="design/backup-screen.png" width="150" title="Backup Setting Info">
|
||||
<img src="design/selective-backup-screen.png" width="150" title="Backup Setting Info">
|
||||
<img src="design/home-screen.jpeg" width="150" title="Home Screen">
|
||||
<img src="design/search-screen.jpeg" width="150" title="Curated Search Info">
|
||||
<img src="design/shared-albums.png" width="150" title="Shared Albums">
|
||||
<img src="design/nsc6.png" width="150" title="EXIF Info">
|
||||
</p>
|
||||
|
||||
### Web client
|
||||
<p align="center">
|
||||
<img src="design/dashboard_photos.jpeg" width="100%" title="Home Dashboard">
|
||||
</p>
|
||||
|
||||
# Note
|
||||
|
||||
**!! NOT READY FOR PRODUCTION! DO NOT USE TO STORE YOUR ASSETS !!**
|
||||
|
||||
This project is under heavy development, there will be continous functions, features and api changes.
|
||||
This project is under heavy development, there will be continuous functions, features and api changes.
|
||||
|
||||
# Features
|
||||
|
||||
- Upload and view assets(videos/images).
|
||||
- Upload and view assets (videos/images).
|
||||
- Auto Backup.
|
||||
- Download asset to local device.
|
||||
- Multi-user supported.
|
||||
- Quick navigation with drag scroll bar.
|
||||
- Auto Backup.
|
||||
- Support HEIC/HEIF Backup.
|
||||
- Extract and display EXIF info.
|
||||
- Real-time render from multi-device upload event.
|
||||
- Image Tagging/Classification based on ImageNet dataset
|
||||
- Object detection based on COCO SSD.
|
||||
- Search assets based on tags and exif data (lens, make, model, orientation)
|
||||
- Upload assets from your local computer/server using [immich cli tools](https://www.npmjs.com/package/immich)
|
||||
- [Optional] Reserve geocoding using Mapbox (Generous free-tier of 100,000 search/month)
|
||||
- [Optional] Reverse geocoding using Mapbox (Generous free-tier of 100,000 search/month)
|
||||
- Show asset's location information on map (OpenStreetMap).
|
||||
- Show curated places on the search page
|
||||
- Show curated objects on the search page
|
||||
- Shared album with users on the same server
|
||||
- Selective backup - albums can be included and excluded during the backup process.
|
||||
- Web interface is available for administrative tasks (creating new users) and viewing assets on the server - additional features are coming.
|
||||
|
||||
# Development
|
||||
# System Requirement
|
||||
|
||||
You can use docker compose for development, there are several services that compose Immich
|
||||
**OS**: Preferred unix-based operating system (Ubuntu, Debian, MacOS...etc).
|
||||
|
||||
1. NestJs
|
||||
2. PostgreSQL
|
||||
3. Redis
|
||||
4. Nginx
|
||||
5. TensorFlow and Keras
|
||||
I haven't tested with `Docker for Windows` as well as `WSL` on Windows
|
||||
|
||||
## Populate .env file
|
||||
*Raspberry Pi can be used but `microservices` container has to be comment out in `docker-compose` since TensorFlow has not been supported in Docker image on arm64v7 yet.*
|
||||
|
||||
**RAM**: At least 2GB, preffered 4GB.
|
||||
|
||||
**Core**: At least 2 cores, preffered 4 cores.
|
||||
|
||||
# Getting Started
|
||||
|
||||
You can use docker compose for development and testing out the application, there are several services that compose Immich:
|
||||
|
||||
1. **NestJs** - Backend of the application
|
||||
2. **SvelteKit** - Web frontend of the application
|
||||
3. **PostgreSQL** - Main database of the application
|
||||
4. **Redis** - For sharing websocket instance between docker instances and background tasks message queue.
|
||||
5. **Nginx** - Load balancing and optimized file uploading.
|
||||
6. **TensorFlow** - Object Detection and Image Classification.
|
||||
|
||||
## Step 1: Populate .env file
|
||||
|
||||
Navigate to `docker` directory and run
|
||||
|
||||
@@ -85,61 +113,151 @@ Notice that if set `ENABLE_MAPBOX` to `true`, you will have to provide `MAPBOX_K
|
||||
|
||||
Pay attention to the key `UPLOAD_LOCATION`, this directory must exist and is owned by the user that run the `docker-compose` command below.
|
||||
|
||||
To start, run
|
||||
**Example**
|
||||
|
||||
```bash
|
||||
docker-compose -f ./docker/docker-compose.yml up --build -V
|
||||
###################################################################################
|
||||
# Database
|
||||
###################################################################################
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=postgres
|
||||
DB_DATABASE_NAME=immich
|
||||
|
||||
###################################################################################
|
||||
# Upload File Config
|
||||
###################################################################################
|
||||
UPLOAD_LOCATION=<put-the-path-of-the-upload-folder-here>
|
||||
|
||||
###################################################################################
|
||||
# JWT SECRET
|
||||
###################################################################################
|
||||
JWT_SECRET=randomstringthatissolongandpowerfulthatnoonecanguess
|
||||
|
||||
###################################################################################
|
||||
# MAPBOX
|
||||
####################################################################################
|
||||
# ENABLE_MAPBOX is either true of false -> if true, you have to provide MAPBOX_KEY
|
||||
ENABLE_MAPBOX=false
|
||||
MAPBOX_KEY=
|
||||
|
||||
###################################################################################
|
||||
# WEB
|
||||
###################################################################################
|
||||
# This is the URL of your vm/server where you host Immich, so that the web frontend
|
||||
# know where can it make the request to.
|
||||
# For example: If your server IP address is 10.1.11.50, the environment variable will
|
||||
# be VITE_SERVER_ENDPOINT=http://10.1.11.50:2283
|
||||
VITE_SERVER_ENDPOINT=http://192.168.1.216:2283
|
||||
```
|
||||
|
||||
## Step 2: Start the server
|
||||
|
||||
To **start**, run
|
||||
|
||||
```bash
|
||||
docker-compose -f ./docker/docker-compose.yml up
|
||||
```
|
||||
|
||||
If you have a few thousand photos/videos, I suggest running docker-compose with *scaling* option for the `immich_server` container to handle high I/O load when using fast scrolling.
|
||||
|
||||
```bash
|
||||
docker-compose -f ./docker/docker-compose.yml up --scale immich-server=5
|
||||
```
|
||||
|
||||
To *update* docker-compose with newest image (if you have started the docker-compose previously)
|
||||
|
||||
```bash
|
||||
docker-compose -f ./docker/docker-compose.yml pull && docker-compose -f ./docker/docker-compose.yml up
|
||||
```
|
||||
|
||||
The server will be running at `http://your-ip:2283` through `Nginx`
|
||||
|
||||
## Register User
|
||||
## Step 3: Register User
|
||||
|
||||
Use the command below on your terminal to create user as we don't have user interface for this function yet.
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://your-server-ip:2283/auth/signUp' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"email": "testuser@email.com",
|
||||
"password": "password"
|
||||
}'
|
||||
```
|
||||
|
||||
## Run mobile app
|
||||
|
||||
### Android
|
||||
|
||||
Download `apk` in release tab and run on your phone. You can follow this guide on how to do that
|
||||
|
||||
- [Run APK on Android](https://www.lifewire.com/install-apk-on-android-4177185)
|
||||
|
||||
### iOS
|
||||
|
||||
You can download the app from Apple AppStore [here](https://apps.apple.com/us/app/immich/id1613945652):
|
||||
Access the web interface at `http://your-ip:2285` to register an admin account.
|
||||
|
||||
<p align="left">
|
||||
<img src="design/ios-qr-code.png" width="250" title="Apple App Store">
|
||||
<img src="design/admin-registration-form.png" width="300" title="Admin Registration">
|
||||
<p/>
|
||||
|
||||
Additional accounts on the server can be created by the admin account.
|
||||
|
||||
<p align="left">
|
||||
<img src="design/admin-interface.png" width="500" title="Admin User Management">
|
||||
<p/>
|
||||
|
||||
## Step 4: Run mobile app
|
||||
|
||||
The app is distributed on several platforms below.
|
||||
|
||||
## F-Droid
|
||||
You can get the app on F-droid by clicking the image below.
|
||||
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Get it on F-Droid"
|
||||
height="80">](https://f-droid.org/packages/app.alextran.immich)
|
||||
|
||||
|
||||
## Android
|
||||
|
||||
#### Get the app on Google Play Store [here](https://play.google.com/store/apps/details?id=app.alextran.immich)
|
||||
|
||||
*The App version might be lagging behind the latest release due to the review process.*
|
||||
|
||||
<p align="left">
|
||||
<img src="design/google-play-qr-code.png" width="200" title="Google Play Store">
|
||||
<p/>
|
||||
|
||||
## iOS
|
||||
|
||||
#### Get the app on Apple AppStore [here](https://apps.apple.com/us/app/immich/id1613945652):
|
||||
|
||||
*The App version might be lagging behind the latest release due to the review process.*
|
||||
|
||||
|
||||
<p align="left">
|
||||
<img src="design/ios-qr-code.png" width="200" title="Apple App Store">
|
||||
<p/>
|
||||
|
||||
|
||||
# Development
|
||||
|
||||
The development environment can be started from the root of the project after populating the `.env` file with the command:
|
||||
|
||||
```bash
|
||||
make dev # required Makefile installed on the system.
|
||||
```
|
||||
|
||||
All servers and web container are hot reload for quick feedback loop.
|
||||
|
||||
# Support
|
||||
|
||||
If you like the app, find it helpful, and want to support me to offset the cost of publishing to AppStores, you can sponsor the project with [**Github Sponsore**](https://github.com/sponsors/alextran1502).
|
||||
If you like the app, find it helpful, and want to support me to offset the cost of publishing to AppStores, you can sponsor the project with [**Github Sponsor**](https://github.com/sponsors/alextran1502), or a one time donation with the Buy Me a coffee link below.
|
||||
|
||||
This is also a meaningful way to give me motivation and encounragment to continue working on the app.
|
||||
[](https://www.buymeacoffee.com/altran1502)
|
||||
|
||||
Cheer! 🎉
|
||||
This is also a meaningful way to give me motivation and encouragement to continue working on the app.
|
||||
|
||||
Cheers! 🎉
|
||||
|
||||
# Known Issue
|
||||
|
||||
TensorFlow doesn't run with older CPU architecture, it requires CPU with AVX and AVX2 instruction set. If you encounter the error `illegal instruction core dump` when running the docker-compose command above, check for your CPU flags with the command and make sure you see `AVX` and `AVX2`. Otherwise, switch to a different VM/desktop with different architecture.
|
||||
## TensorFlow Build Issue
|
||||
|
||||
*This is a known issue on RaspberryPi 4 arm64-v7 and incorrect Promox setup*
|
||||
|
||||
TensorFlow doesn't run with older CPU architecture, it requires a CPU with AVX and AVX2 instruction set. If you encounter the error `illegal instruction core dump` when running the docker-compose command above, check for your CPU flags with the command and make sure you see `AVX` and `AVX2`:
|
||||
|
||||
```bash
|
||||
more /proc/cpuinfo | grep flags
|
||||
```
|
||||
|
||||
If you are running virtualization in Promox, the VM doesn't have the flag enable.
|
||||
|
||||
```
|
||||
|
||||
If you are running virtualization in Promox, the VM doesn't have the flag enabled.
|
||||
|
||||
You need to change the CPU type from `kvm64` to `host` under VMs hardware tab.
|
||||
|
||||
|
||||
`Hardware > Processors > Edit > Advanced > Type (dropdown menu) > host`
|
||||
|
||||
Otherwise you can:
|
||||
- edit `docker-compose.yml` file and comment the whole `immich-machine-learning` service **which will disable machine learning features like object detection and image classification**
|
||||
- switch to a different VM/desktop with different architecture.
|
||||
|
||||
BIN
design/admin-interface.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
design/admin-registration-form.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
design/backup-screen.png
Normal file
|
After Width: | Height: | Size: 308 KiB |
BIN
design/dashboard_photos.jpeg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
design/feature-panel.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
design/google-play-qr-code.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
design/home-screen.jpeg
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
design/login-screen.png
Normal file
|
After Width: | Height: | Size: 278 KiB |
BIN
design/nsc1.png
|
Before Width: | Height: | Size: 176 KiB |
BIN
design/nsc2.png
|
Before Width: | Height: | Size: 303 KiB |
BIN
design/nsc4.jpeg
Normal file
|
After Width: | Height: | Size: 406 KiB |
BIN
design/search-screen.jpeg
Normal file
|
After Width: | Height: | Size: 376 KiB |
BIN
design/selective-backup-screen.png
Normal file
|
After Width: | Height: | Size: 570 KiB |
BIN
design/shared-albums.png
Normal file
|
After Width: | Height: | Size: 244 KiB |
@@ -1,18 +1,63 @@
|
||||
# STAGE
|
||||
NODE_ENV=development
|
||||
|
||||
###################################################################################
|
||||
# Database
|
||||
###################################################################################
|
||||
|
||||
DB_HOSTNAME=immich_postgres
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=postgres
|
||||
DB_DATABASE_NAME=
|
||||
DB_DATABASE_NAME=immich
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
###################################################################################
|
||||
# Redis
|
||||
###################################################################################
|
||||
|
||||
REDIS_HOSTNAME=immich_redis
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
###################################################################################
|
||||
# Upload File Config
|
||||
###################################################################################
|
||||
|
||||
UPLOAD_LOCATION=absolute_location_on_your_machine_where_you_want_to_store_the_backup
|
||||
|
||||
# JWT SECRET
|
||||
JWT_SECRET=
|
||||
|
||||
|
||||
|
||||
###################################################################################
|
||||
# JWT SECRET
|
||||
###################################################################################
|
||||
|
||||
JWT_SECRET=randomstringthatissolongandpowerfulthatnoonecanguess
|
||||
|
||||
|
||||
|
||||
|
||||
###################################################################################
|
||||
# MAPBOX
|
||||
## ENABLE_MAPBOX is either true of false -> if true, you have to provide MAPBOX_KEY
|
||||
ENABLE_MAPBOX=
|
||||
MAPBOX_KEY=
|
||||
####################################################################################
|
||||
|
||||
# ENABLE_MAPBOX is either true of false -> if true, you have to provide MAPBOX_KEY
|
||||
ENABLE_MAPBOX=false
|
||||
MAPBOX_KEY=
|
||||
|
||||
|
||||
|
||||
|
||||
###################################################################################
|
||||
# WEB
|
||||
###################################################################################
|
||||
|
||||
# This is the URL of your vm/server where you host Immich, so that the web frontend
|
||||
# know where can it make the request to.
|
||||
# For example: If your server IP address is 10.1.11.50, the environment variable will
|
||||
# be VITE_SERVER_ENDPOINT=http://10.1.11.50:2283
|
||||
# !CAUTION! THERE IS NO FORWARD SLASH AT THE END
|
||||
|
||||
VITE_SERVER_ENDPOINT=
|
||||
|
||||
22
docker/.env.test
Normal file
@@ -0,0 +1,22 @@
|
||||
# Database
|
||||
DB_HOSTNAME=immich_postgres_test
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=postgres
|
||||
DB_DATABASE_NAME=e2e_test
|
||||
|
||||
# Redis
|
||||
REDIS_HOSTNAME=immich_redis_test
|
||||
|
||||
# Upload File Config
|
||||
UPLOAD_LOCATION=./upload
|
||||
|
||||
# JWT SECRET
|
||||
JWT_SECRET=randomstringthatissolongandpowerfulthatnoonecanguess
|
||||
|
||||
# MAPBOX
|
||||
## ENABLE_MAPBOX is either true of false -> if true, you have to provide MAPBOX_KEY
|
||||
ENABLE_MAPBOX=false
|
||||
|
||||
# WEB
|
||||
MAPBOX_KEY=
|
||||
VITE_SERVER_ENDPOINT=http://localhost:2283
|
||||
126
docker/docker-compose.dev.yml
Normal file
@@ -0,0 +1,126 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
image: immich-server-dev:1.9.0
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
command: npm run start:dev immich
|
||||
expose:
|
||||
- "3000"
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
networks:
|
||||
- immich-network
|
||||
|
||||
immich-machine-learning:
|
||||
image: immich-machine-learning-dev:1.9.0
|
||||
build:
|
||||
context: ../machine-learning
|
||||
dockerfile: Dockerfile
|
||||
command: npm run start:dev
|
||||
expose:
|
||||
- "3001"
|
||||
volumes:
|
||||
- ../machine-learning:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
depends_on:
|
||||
- database
|
||||
networks:
|
||||
- immich-network
|
||||
|
||||
immich-microservices:
|
||||
image: immich-microservices:1.9.0
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
command: npm run start:dev microservices
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
depends_on:
|
||||
- database
|
||||
networks:
|
||||
- immich-network
|
||||
|
||||
immich-web:
|
||||
image: immich-web-dev:1.9.0
|
||||
build:
|
||||
context: ../web
|
||||
dockerfile: Dockerfile
|
||||
target: dev
|
||||
command: npm run dev --host
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 3002:3002
|
||||
- 24678:24678
|
||||
volumes:
|
||||
- ../web:/usr/src/app
|
||||
- /usr/src/app/node_modules
|
||||
networks:
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2
|
||||
networks:
|
||||
- immich-network
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: postgres:14
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||
PG_DATA: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
ports:
|
||||
- 5432:5432
|
||||
networks:
|
||||
- immich-network
|
||||
|
||||
nginx:
|
||||
container_name: proxy_nginx
|
||||
image: nginx:latest
|
||||
volumes:
|
||||
- ./settings/nginx-conf:/etc/nginx/conf.d
|
||||
ports:
|
||||
- 2283:80
|
||||
- 2284:443
|
||||
logging:
|
||||
driver: none
|
||||
networks:
|
||||
- immich-network
|
||||
depends_on:
|
||||
- immich-server
|
||||
|
||||
networks:
|
||||
immich-network:
|
||||
volumes:
|
||||
pgdata:
|
||||
@@ -1,12 +1,11 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
immich_server:
|
||||
image: immich-server-dev:1.3.0
|
||||
immich-server:
|
||||
image: immich-server-dev:1.9.0
|
||||
build:
|
||||
context: ../server
|
||||
target: development
|
||||
dockerfile: ../server/Dockerfile
|
||||
dockerfile: Dockerfile
|
||||
command: npm run start:dev
|
||||
expose:
|
||||
- "3000"
|
||||
@@ -20,13 +19,40 @@ services:
|
||||
- redis
|
||||
- database
|
||||
networks:
|
||||
- immich_network
|
||||
- immich-network
|
||||
|
||||
immich-microservices:
|
||||
image: immich-microservices-dev:1.9.0
|
||||
build:
|
||||
context: ../microservices
|
||||
dockerfile: Dockerfile
|
||||
command: npm run start:dev
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [ gpu ]
|
||||
expose:
|
||||
- "3001"
|
||||
volumes:
|
||||
- ../microservices:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
- database
|
||||
- immich_server
|
||||
networks:
|
||||
- immich-network
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2
|
||||
networks:
|
||||
- immich_network
|
||||
- immich-network
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
@@ -43,7 +69,7 @@ services:
|
||||
ports:
|
||||
- 5432:5432
|
||||
networks:
|
||||
- immich_network
|
||||
- immich-network
|
||||
|
||||
nginx:
|
||||
container_name: proxy_nginx
|
||||
@@ -56,40 +82,11 @@ services:
|
||||
logging:
|
||||
driver: none
|
||||
networks:
|
||||
- immich_network
|
||||
- immich-network
|
||||
depends_on:
|
||||
- immich_server
|
||||
- immich-server
|
||||
|
||||
immich_tf_fastapi:
|
||||
container_name: immich_tf_fastapi
|
||||
image: tensor_flow_fastapi:1.0.0
|
||||
restart: always
|
||||
command: uvicorn app.main:app --proxy-headers --host 0.0.0.0 --port 8000 --reload
|
||||
build:
|
||||
context: ../machine_learning
|
||||
target: gpu
|
||||
dockerfile: ../machine_learning/Dockerfile
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [gpu]
|
||||
volumes:
|
||||
- ../machine_learning/app:/code/app
|
||||
- ${UPLOAD_LOCATION}:/code/app/upload
|
||||
ports:
|
||||
- 2285:8000
|
||||
expose:
|
||||
- "8000"
|
||||
depends_on:
|
||||
- database
|
||||
networks:
|
||||
- immich_network
|
||||
|
||||
|
||||
networks:
|
||||
immich_network:
|
||||
immich-network:
|
||||
volumes:
|
||||
pgdata:
|
||||
|
||||
110
docker/docker-compose.staging.yml
Normal file
@@ -0,0 +1,110 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
image: altran1502/immich-server:staging
|
||||
entrypoint: ["/bin/sh", "./start-server.sh"]
|
||||
expose:
|
||||
- "3000"
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
networks:
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
immich-microservices:
|
||||
image: altran1502/immich-server:staging
|
||||
entrypoint: ["/bin/sh", "./start-microservices.sh"]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
networks:
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
immich-machine-learning:
|
||||
image: altran1502/immich-machine-learning:staging
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
expose:
|
||||
- "3001"
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- database
|
||||
networks:
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
immich-web:
|
||||
image: altran1502/immich-web:staging
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 2285:3000
|
||||
networks:
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2
|
||||
networks:
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: postgres:14
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||
PG_DATA: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
ports:
|
||||
- 5432:5432
|
||||
networks:
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
nginx:
|
||||
container_name: proxy_nginx
|
||||
image: nginx:latest
|
||||
volumes:
|
||||
- ./settings/nginx-conf:/etc/nginx/conf.d
|
||||
ports:
|
||||
- 2283:80
|
||||
- 2284:443
|
||||
logging:
|
||||
driver: none
|
||||
networks:
|
||||
- immich-network
|
||||
depends_on:
|
||||
- immich-server
|
||||
restart: always
|
||||
|
||||
networks:
|
||||
immich-network:
|
||||
volumes:
|
||||
pgdata:
|
||||
50
docker/docker-compose.test.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
immich_server_test:
|
||||
image: immich-server-dev:1.9.0
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
command: npm run test:e2e
|
||||
expose:
|
||||
- "3000"
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env.test
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
networks:
|
||||
- immich_network_test
|
||||
|
||||
|
||||
redis:
|
||||
container_name: immich_redis_test
|
||||
image: redis:6.2
|
||||
networks:
|
||||
- immich_network_test
|
||||
|
||||
database:
|
||||
container_name: immich_postgres_test
|
||||
image: postgres:14
|
||||
env_file:
|
||||
- .env.test
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||
PG_DATA: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- /var/lib/postgresql/data
|
||||
ports:
|
||||
- 5432:5432
|
||||
networks:
|
||||
- immich_network_test
|
||||
|
||||
networks:
|
||||
immich_network_test:
|
||||
@@ -1,33 +1,74 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
immich_server:
|
||||
image: immich-server-dev:1.3.0
|
||||
build:
|
||||
context: ../server
|
||||
target: development
|
||||
dockerfile: ../server/Dockerfile
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
# command: npm run start:dev
|
||||
immich-server:
|
||||
image: altran1502/immich-server:latest
|
||||
entrypoint: ["/bin/sh", "./start-server.sh"]
|
||||
expose:
|
||||
- "3000"
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
networks:
|
||||
- immich_network
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
immich-microservices:
|
||||
image: altran1502/immich-server:latest
|
||||
entrypoint: ["/bin/sh", "./start-microservices.sh"]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
networks:
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
immich-machine-learning:
|
||||
image: altran1502/immich-machine-learning:latest
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
expose:
|
||||
- "3001"
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- database
|
||||
networks:
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
immich-web:
|
||||
image: altran1502/immich-web:latest
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 2285:3000
|
||||
networks:
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2
|
||||
networks:
|
||||
- immich_network
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
@@ -44,7 +85,8 @@ services:
|
||||
ports:
|
||||
- 5432:5432
|
||||
networks:
|
||||
- immich_network
|
||||
- immich-network
|
||||
restart: always
|
||||
|
||||
nginx:
|
||||
container_name: proxy_nginx
|
||||
@@ -57,32 +99,12 @@ services:
|
||||
logging:
|
||||
driver: none
|
||||
networks:
|
||||
- immich_network
|
||||
- immich-network
|
||||
depends_on:
|
||||
- immich_server
|
||||
|
||||
immich_tf_fastapi:
|
||||
container_name: immich_tf_fastapi
|
||||
image: tensor_flow_fastapi:1.0.0
|
||||
- immich-server
|
||||
restart: always
|
||||
command: uvicorn app.main:app --proxy-headers --host 0.0.0.0 --port 8000 --reload
|
||||
build:
|
||||
context: ../machine_learning
|
||||
target: cpu
|
||||
dockerfile: ../machine_learning/Dockerfile
|
||||
volumes:
|
||||
- ../machine_learning/app:/code/app
|
||||
- ${UPLOAD_LOCATION}:/code/app/upload
|
||||
ports:
|
||||
- 2285:8000
|
||||
expose:
|
||||
- "8000"
|
||||
depends_on:
|
||||
- database
|
||||
networks:
|
||||
- immich_network
|
||||
|
||||
networks:
|
||||
immich_network:
|
||||
immich-network:
|
||||
volumes:
|
||||
pgdata:
|
||||
|
||||
@@ -10,11 +10,22 @@ map $http_upgrade $connection_upgrade {
|
||||
|
||||
server {
|
||||
|
||||
gzip on;
|
||||
gzip_min_length 1000;
|
||||
gunzip on;
|
||||
|
||||
client_max_body_size 50000M;
|
||||
|
||||
listen 80;
|
||||
access_log off;
|
||||
|
||||
location / {
|
||||
|
||||
# Compression
|
||||
gzip_static on;
|
||||
gzip_min_length 1000;
|
||||
gzip_comp_level 2;
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_busy_buffers_size 24k;
|
||||
@@ -30,6 +41,6 @@ server {
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
|
||||
proxy_pass http://immich_server:3000;
|
||||
proxy_pass http://immich-server:3000;
|
||||
}
|
||||
}
|
||||
|
||||
3
fastlane/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
This directory exists because of the F-Droid build process. F-Droid is using the same directory structure as Fastlane for the app metadata.
|
||||
|
||||
Because F-Droid expects the metadata to be located in the root of the repository we need to have this symlink.
|
||||
1
fastlane/metadata
Symbolic link
@@ -0,0 +1 @@
|
||||
../mobile/android/fastlane/metadata
|
||||
4
machine-learning/.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
upload/
|
||||
dist/
|
||||
|
||||
24
machine-learning/.eslintrc.js
Normal file
@@ -0,0 +1,24 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
};
|
||||
37
machine-learning/.gitignore
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
upload/
|
||||
4
machine-learning/.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
16
machine-learning/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM node:16-bullseye-slim
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install gcc g++ make cmake python3 python3-pip ffmpeg -y
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
4
machine-learning/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
# Microservices for Immich
|
||||
|
||||
## Image Classifier
|
||||
2
machine-learning/entrypoint.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
# npm run typeorm migration:run
|
||||
npm run build && npm run start:prod
|
||||
4
machine-learning/nest-cli.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src"
|
||||
}
|
||||
17323
machine-learning/package-lock.json
generated
Normal file
83
machine-learning/package.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"name": "nest_microservices",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^8.0.0",
|
||||
"@nestjs/core": "^8.0.0",
|
||||
"@nestjs/mapped-types": "^1.0.1",
|
||||
"@nestjs/platform-express": "^8.0.0",
|
||||
"@nestjs/typeorm": "^8.0.3",
|
||||
"@tensorflow-models/coco-ssd": "^2.2.2",
|
||||
"@tensorflow-models/mobilenet": "^2.1.0",
|
||||
"@tensorflow/tfjs": "^3.15.0",
|
||||
"@tensorflow/tfjs-converter": "^3.15.0",
|
||||
"@tensorflow/tfjs-core": "^3.15.0",
|
||||
"@tensorflow/tfjs-node": "^3.15.0",
|
||||
"@tensorflow/tfjs-node-gpu": "^3.15.0",
|
||||
"@trpc/server": "^9.20.3",
|
||||
"pg": "^8.7.3",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.2.0",
|
||||
"typeorm": "^0.2.45"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^8.2.4",
|
||||
"@nestjs/schematics": "^8.0.0",
|
||||
"@nestjs/testing": "^8.0.0",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.2.5",
|
||||
"prettier": "^2.3.2",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "^27.0.3",
|
||||
"ts-loader": "^9.2.3",
|
||||
"ts-node": "^10.0.0",
|
||||
"tsconfig-paths": "^3.10.1",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
16
machine-learning/src/app.module.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ImageClassifierModule } from './image-classifier/image-classifier.module';
|
||||
import { databaseConfig } from './config/database.config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ObjectDetectionModule } from './object-detection/object-detection.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forRoot(databaseConfig),
|
||||
ImageClassifierModule,
|
||||
ObjectDetectionModule,
|
||||
],
|
||||
controllers: [],
|
||||
providers: [],
|
||||
})
|
||||
export class AppModule {}
|
||||
11
machine-learning/src/config/database.config.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||
|
||||
export const databaseConfig: TypeOrmModuleOptions = {
|
||||
type: 'postgres',
|
||||
host: process.env.DB_HOSTNAME || 'immich_postgres',
|
||||
port: 5432,
|
||||
username: process.env.DB_USERNAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_DATABASE_NAME,
|
||||
synchronize: false,
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Body, Controller, Post } from '@nestjs/common';
|
||||
import { ImageClassifierService } from './image-classifier.service';
|
||||
|
||||
@Controller('image-classifier')
|
||||
export class ImageClassifierController {
|
||||
constructor(
|
||||
private readonly imageClassifierService: ImageClassifierService,
|
||||
) { }
|
||||
|
||||
@Post('/tag-image')
|
||||
async tagImage(@Body('thumbnailPath') thumbnailPath: string) {
|
||||
return await this.imageClassifierService.tagImage(thumbnailPath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ImageClassifierService } from './image-classifier.service';
|
||||
import { ImageClassifierController } from './image-classifier.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [ImageClassifierController],
|
||||
providers: [ImageClassifierService],
|
||||
})
|
||||
export class ImageClassifierModule {}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import * as mobilenet from '@tensorflow-models/mobilenet';
|
||||
import * as cocoSsd from '@tensorflow-models/coco-ssd';
|
||||
import * as tf from '@tensorflow/tfjs-node';
|
||||
import * as fs from 'fs';
|
||||
|
||||
@Injectable()
|
||||
export class ImageClassifierService {
|
||||
private readonly MOBILENET_VERSION = 2;
|
||||
private readonly MOBILENET_ALPHA = 1.0;
|
||||
|
||||
private mobileNetModel: mobilenet.MobileNet;
|
||||
|
||||
constructor() {
|
||||
Logger.log(
|
||||
`Running Node TensorFlow Version : ${tf.version['tfjs']}`,
|
||||
'ImageClassifier',
|
||||
);
|
||||
mobilenet
|
||||
.load({
|
||||
version: this.MOBILENET_VERSION,
|
||||
alpha: this.MOBILENET_ALPHA,
|
||||
})
|
||||
.then((mobilenetModel) => (this.mobileNetModel = mobilenetModel));
|
||||
}
|
||||
|
||||
async tagImage(thumbnailPath: string) {
|
||||
try {
|
||||
const isExist = fs.existsSync(thumbnailPath);
|
||||
if (isExist) {
|
||||
const tags = [];
|
||||
const image = fs.readFileSync(thumbnailPath);
|
||||
const decodedImage = tf.node.decodeImage(image, 3) as tf.Tensor3D;
|
||||
const predictions = await this.mobileNetModel.classify(decodedImage);
|
||||
|
||||
for (const prediction of predictions) {
|
||||
if (prediction.probability >= 0.1) {
|
||||
tags.push(...prediction.className.split(',').map((e) => e.trim()));
|
||||
}
|
||||
}
|
||||
|
||||
tf.dispose(decodedImage);
|
||||
return tags;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Error reading file ', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
machine-learning/src/main.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
await app.listen(3001, () => {
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
Logger.log(
|
||||
'Running Immich Machine Learning in DEVELOPMENT environment',
|
||||
'IMMICH MICROSERVICES',
|
||||
);
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV == 'production') {
|
||||
Logger.log(
|
||||
'Running Immich Machine Learning in PRODUCTION environment',
|
||||
'IMMICH MICROSERVICES',
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Body, Controller, Post } from '@nestjs/common';
|
||||
import { ObjectDetectionService } from './object-detection.service';
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
@Controller('object-detection')
|
||||
export class ObjectDetectionController {
|
||||
constructor(
|
||||
private readonly objectDetectionService: ObjectDetectionService,
|
||||
) { }
|
||||
|
||||
@Post('/detect-object')
|
||||
async detectObject(@Body('thumbnailPath') thumbnailPath: string) {
|
||||
return await this.objectDetectionService.detectObject(thumbnailPath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ObjectDetectionService } from './object-detection.service';
|
||||
import { ObjectDetectionController } from './object-detection.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [ObjectDetectionController],
|
||||
providers: [ObjectDetectionService],
|
||||
})
|
||||
export class ObjectDetectionModule {}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import * as cocoSsd from '@tensorflow-models/coco-ssd';
|
||||
import * as tf from '@tensorflow/tfjs-node';
|
||||
import * as fs from 'fs';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectDetectionService {
|
||||
private cocoSsdModel: cocoSsd.ObjectDetection;
|
||||
|
||||
constructor() {
|
||||
Logger.log(
|
||||
`Running Node TensorFlow Version : ${tf.version['tfjs']}`,
|
||||
'ObjectDetection',
|
||||
);
|
||||
cocoSsd.load().then((model) => (this.cocoSsdModel = model));
|
||||
}
|
||||
async detectObject(thumbnailPath: string) {
|
||||
try {
|
||||
const isExist = fs.existsSync(thumbnailPath);
|
||||
if (isExist) {
|
||||
const tags = new Set();
|
||||
const image = fs.readFileSync(thumbnailPath);
|
||||
const decodedImage = tf.node.decodeImage(image, 3) as tf.Tensor3D;
|
||||
const predictions = await this.cocoSsdModel.detect(decodedImage);
|
||||
|
||||
for (const result of predictions) {
|
||||
if (result.score > 0.5) {
|
||||
tags.add(result.class);
|
||||
}
|
||||
}
|
||||
|
||||
tf.dispose(decodedImage);
|
||||
return [...tags];
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Error reading file ', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
4
machine-learning/tsconfig.build.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
||||
21
machine-learning/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "es2017",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
devenv/
|
||||
3
machine_learning/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
__pycache__/
|
||||
devenv/
|
||||
app/upload
|
||||
@@ -1,25 +0,0 @@
|
||||
## GPU Build
|
||||
# FROM tensorflow/tensorflow:latest-gpu as gpu
|
||||
|
||||
# WORKDIR /code
|
||||
|
||||
# COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
# RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# COPY ./app /code/app
|
||||
|
||||
|
||||
## CPU BUILD
|
||||
FROM python:3.8 as cpu
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install ffmpeg libsm6 libxext6 -y
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
COPY ./app /code/app
|
||||
|
Before Width: | Height: | Size: 193 KiB |
@@ -1,37 +0,0 @@
|
||||
from tensorflow.keras.applications import InceptionV3
|
||||
from tensorflow.keras.applications.inception_v3 import preprocess_input, decode_predictions
|
||||
from tensorflow.keras.preprocessing import image
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import cv2
|
||||
IMG_SIZE = 299
|
||||
PREDICTION_MODEL = InceptionV3(weights='imagenet')
|
||||
|
||||
|
||||
def classify_image(image_path: str):
|
||||
img_path = f'./app/{image_path}'
|
||||
# img = image.load_img(img_path, target_size=(IMG_SIZE, IMG_SIZE))
|
||||
|
||||
target_image = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
|
||||
resized_target_image = cv2.resize(target_image, (IMG_SIZE, IMG_SIZE))
|
||||
|
||||
x = image.img_to_array(resized_target_image)
|
||||
x = np.expand_dims(x, axis=0)
|
||||
x = preprocess_input(x)
|
||||
|
||||
preds = PREDICTION_MODEL.predict(x)
|
||||
result = decode_predictions(preds, top=3)[0]
|
||||
payload = []
|
||||
for _, value, _ in result:
|
||||
payload.append(value)
|
||||
|
||||
return payload
|
||||
|
||||
|
||||
def warm_up():
|
||||
img_path = f'./app/test.png'
|
||||
img = image.load_img(img_path, target_size=(IMG_SIZE, IMG_SIZE))
|
||||
x = image.img_to_array(img)
|
||||
x = np.expand_dims(x, axis=0)
|
||||
x = preprocess_input(x)
|
||||
PREDICTION_MODEL.predict(x)
|
||||
@@ -1,46 +0,0 @@
|
||||
from pydantic import BaseModel
|
||||
from fastapi import FastAPI
|
||||
|
||||
from .object_detection import object_detection
|
||||
from .image_classifier import image_classifier
|
||||
|
||||
from tf2_yolov4.anchors import YOLOV4_ANCHORS
|
||||
from tf2_yolov4.model import YOLOv4
|
||||
|
||||
|
||||
HEIGHT, WIDTH = (640, 960)
|
||||
|
||||
# Warm up model
|
||||
image_classifier.warm_up()
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class TagImagePayload(BaseModel):
|
||||
thumbnail_path: str
|
||||
|
||||
|
||||
@app.post("/tagImage")
|
||||
async def post_root(payload: TagImagePayload):
|
||||
image_path = payload.thumbnail_path
|
||||
|
||||
if image_path[0] == '.':
|
||||
image_path = image_path[2:]
|
||||
|
||||
return image_classifier.classify_image(image_path=image_path)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def test():
|
||||
|
||||
object_detection.run_detection()
|
||||
# image = tf.io.read_file("./app/cars.jpg")
|
||||
# image = tf.image.decode_image(image)
|
||||
# image = tf.image.resize(image, (HEIGHT, WIDTH))
|
||||
# images = tf.expand_dims(image, axis=0) / 255.0
|
||||
|
||||
# model = YOLOv4(
|
||||
# (HEIGHT, WIDTH, 3),
|
||||
# 80,
|
||||
# YOLOV4_ANCHORS,
|
||||
# "darknet",
|
||||
# )
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
|
||||
def run_detection():
|
||||
print("run detection")
|
||||
|
Before Width: | Height: | Size: 345 KiB |
@@ -1,8 +0,0 @@
|
||||
opencv-python==4.5.5.64
|
||||
fastapi>=0.68.0,<0.69.0
|
||||
pydantic>=1.8.0,<2.0.0
|
||||
uvicorn>=0.15.0,<0.16.0
|
||||
tensorflow==2.8.0
|
||||
numpy==1.22.2
|
||||
pillow==9.0.1
|
||||
tf2_yolov4==0.1.0
|
||||
@@ -51,7 +51,7 @@ android {
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "app.alextran.immich"
|
||||
minSdkVersion 20
|
||||
minSdkVersion 21
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
@@ -81,4 +81,5 @@ flutter {
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="app.alextran.immich">
|
||||
<application android:label="Immich" android:name="${applicationName}" android:usesCleartextTraffic="true" android:icon="@mipmap/ic_launcher">
|
||||
<application android:label="Immich" android:name="${applicationName}" android:usesCleartextTraffic="true" android:icon="@mipmap/ic_launcher" android:requestLegacyExternalStorage="true">
|
||||
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
@@ -20,4 +20,14 @@
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
@@ -1,16 +1,21 @@
|
||||
// Generated file.
|
||||
//
|
||||
// If you wish to remove Flutter's multidex support, delete this entire file.
|
||||
//
|
||||
// Modifications to this file should be done in a copy under a different name
|
||||
// as this file may be regenerated.
|
||||
|
||||
package io.flutter.app;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.multidex.MultiDex;
|
||||
|
||||
/**
|
||||
* Extension of {@link io.flutter.app.FlutterApplication}, adding multidex support.
|
||||
* Extension of {@link android.app.Application}, adding multidex support.
|
||||
*/
|
||||
public class FlutterMultiDexApplication extends FlutterApplication {
|
||||
public class FlutterMultiDexApplication extends Application {
|
||||
@Override
|
||||
@CallSuper
|
||||
protected void attachBaseContext(Context base) {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
* Accepting webp file format
|
||||
* Fixed backup stop when an asset is of wrong file type. The app will now skip that asset and try its best to perform the backup operation on the rest of the assets.
|
||||
@@ -0,0 +1,7 @@
|
||||
* New features
|
||||
- Share album. Users can now create albums to share with existing people on the network.
|
||||
- Owner can delete the album.
|
||||
- Owner can invite the additional users to the album.
|
||||
- Shared users and the owner can add additional assets to the album.
|
||||
* In the asset viewer, the user can swipe up to see detailed information and swip down to dismiss.
|
||||
* Several UI enhancements.
|
||||
@@ -0,0 +1 @@
|
||||
* Album name is now editable
|
||||
@@ -0,0 +1,2 @@
|
||||
* New Feature - Selection backup. User can now select a combination of albums to be included or excluded during the backup process, and only unique photos, and videos that are not overlapping between the two groups will be backup.
|
||||
* Bug fix - Show correct count of backup and remainder assets.
|
||||
@@ -0,0 +1 @@
|
||||
* Hotfix: Permission is being requested now when open backup screen on Android10
|
||||
@@ -0,0 +1 @@
|
||||
* User can now upload profile picture from the home page control drawer.
|
||||
@@ -0,0 +1,2 @@
|
||||
* Update to Material Design 3
|
||||
* Fixed back button navigation - no longer return back to the home page
|
||||
@@ -0,0 +1 @@
|
||||
* Added announcement pop-up when a new released is pushed out in Github.
|
||||
@@ -0,0 +1,2 @@
|
||||
* Fixed crash issue when upload large file on slow network
|
||||
* Updated album to conform with server refactoring of SharedAlbum to Album
|
||||
@@ -0,0 +1 @@
|
||||
* Added curated locations and objects on the search page
|
||||
@@ -0,0 +1,2 @@
|
||||
* User can now download assets to local device
|
||||
* Increased the font size for curated image thumbnail information on the seach page
|
||||
@@ -0,0 +1 @@
|
||||
* Added inline font, remove google-font dependency in pubspec.
|
||||
@@ -0,0 +1,21 @@
|
||||
This is a client app for the self-hostable Immich Server (which can be found with the app's source repo). You will need to run/manage the server on your own in order to use the app.
|
||||
|
||||
Once set up, this app can be used as photo and video backup solution directly from your mobile phone.
|
||||
|
||||
<b>Features:</b>
|
||||
|
||||
* Upload and view assets(videos/images).
|
||||
* Multi-user supported.
|
||||
* Quick navigation with drag scroll bar.
|
||||
* Auto Backup.
|
||||
* Support HEIC/HEIF Backup.
|
||||
* Extract and display EXIF info.
|
||||
* Real-time render from multi-device upload event.
|
||||
* Image Tagging/Classification based on ImageNet dataset
|
||||
* Object detection based on COCO SSD.
|
||||
* Search assets based on tags and exif data (lens, make, model, orientation)
|
||||
* Upload assets from your local computer/server using <a href='https://www.npmjs.com/package/immich' target='_blank' rel='nofollow'>immich cli tools</a>
|
||||
* [Optional] Reserve geocoding using Mapbox (Generous free-tier of 100,000 search/month)
|
||||
* Show asset's location information on map (OpenStreetMap).
|
||||
* Show curated places on the search page
|
||||
* Show curated objects on the search page
|
||||
|
After Width: | Height: | Size: 48 KiB |
BIN
mobile/android/fastlane/metadata/android/en-US/images/icon.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 299 KiB |
|
After Width: | Height: | Size: 681 KiB |
|
After Width: | Height: | Size: 252 KiB |
|
After Width: | Height: | Size: 517 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 3.3 MiB |
|
After Width: | Height: | Size: 185 KiB |
|
After Width: | Height: | Size: 3.7 MiB |
|
After Width: | Height: | Size: 1.4 MiB |