Compare commits

...

4 Commits

Author SHA1 Message Date
Alex
c7dff229db Up minor v1.6.0 (#113) 2022-04-05 10:34:54 -05:00
Alex
8e80825b4f Build and tag docker image for Dockerhub release (#111)
* Clean up Dockerfile and added action to build microservice latest
* Combine build microservices and server into the same action
* Added build and push release version for microservices
2022-04-05 10:16:15 -05:00
Constantin Kraft
a1481c1113 Fix typo: Reserve -> Reverse (geocoding) (#112) 2022-04-05 10:11:40 -05:00
Alex
3bdcdef198 Fixed backup stuck at unsupported format (#108)
* Added webp as supported file type, allow continue upload when an image fail

* Added webp as supported file type, allow continue upload when an image fail

* Solved issue with bad assets cause backup to stop
2022-04-04 23:37:48 -05:00
17 changed files with 200 additions and 119 deletions

View File

@@ -0,0 +1,64 @@
name: Build and Push Docker Image - Latest
on:
workflow_dispatch:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build_and_push_server_latest:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: "main" # branch
- name: Set up QEMU
uses: docker/setup-qemu-action@v1.2.0
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1.6.0
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Immich
uses: docker/build-push-action@v2.10.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:latest
build_and_push_microservice_latest:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: "main" # branch
- name: Set up QEMU
uses: docker/setup-qemu-action@v1.2.0
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1.6.0
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and Push Microservices
uses: docker/build-push-action@v2.10.0
with:
context: ./microservices
file: ./microservices/Dockerfile
platforms: linux/arm/v7,linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: |
altran1502/immich-microservices:latest

View File

@@ -1,42 +0,0 @@
name: Build Server - Latest
on:
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

View File

@@ -2,37 +2,94 @@ name: Build Server - Release
on: on:
workflow_dispatch: workflow_dispatch:
release:
types: [published]
jobs: jobs:
buildandpush: build_and_push_server_release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
ref: "main" # branch ref: "main"
# https://github.com/docker/setup-qemu-action#usage fetch-depth: 0
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: altran1502/immich-server
- name: 'Get Previous tag'
id: previoustag
uses: "WyriHaximus/github-action-get-previous-tag@v1"
with:
fallback: latest
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1.2.0 uses: docker/setup-qemu-action@v1.2.0
# https://github.com/marketplace/actions/docker-setup-buildx
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v1.6.0 uses: docker/setup-buildx-action@v1.6.0
# https://github.com/docker/login-action#docker-hub
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v1 uses: docker/login-action@v1
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
# https://github.com/docker/build-push-action#multi-platform-image
- name: Build and push Immich - name: Build and push immich-server release
uses: docker/build-push-action@v2.10.0 uses: docker/build-push-action@v2.10.0
with: with:
context: ./server context: ./server
file: ./server/Dockerfile 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 platforms: linux/arm/v7,linux/amd64,linux/arm64
pull: true push: ${{ github.event_name != 'pull_request' }}
push: true tags: ${{ steps.previoustag.outputs.tag }}
tags: | labels: ${{ steps.meta.outputs.labels }}
altran1502/immich-server:${{github.ref_name}}
build_and_push_microservice_release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: "main"
fetch-depth: 0
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: altran1502/immich-microservices
- 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@v1.2.0
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1.6.0
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push immich-microservices release
uses: docker/build-push-action@v2.10.0
with:
context: ./microservices
file: ./microservices/Dockerfile
platforms: linux/arm/v7,linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.previoustag.outputs.tag }}
labels: ${{ steps.meta.outputs.labels }}

View File

@@ -59,7 +59,7 @@ This project is under heavy development, there will be continous functions, feat
- Object detection based on COCO SSD. - Object detection based on COCO SSD.
- Search assets based on tags and exif data (lens, make, model, orientation) - 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) - 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 asset's location information on map (OpenStreetMap).
- Show curated places on the search page - Show curated places on the search page
- Show curated objects on the search page - Show curated objects on the search page

View File

@@ -2,7 +2,7 @@ version: "3.8"
services: services:
immich_server: immich_server:
image: immich-server-dev:1.5.0 image: immich-server-dev:1.6.0
build: build:
context: ../server context: ../server
dockerfile: Dockerfile dockerfile: Dockerfile
@@ -24,7 +24,7 @@ services:
- immich_network - immich_network
immich_microservices: immich_microservices:
image: immich-microservices-dev:1.5.0 image: immich-microservices-dev:1.6.0
build: build:
context: ../microservices context: ../microservices
dockerfile: Dockerfile dockerfile: Dockerfile

View File

@@ -2,7 +2,7 @@ version: "3.8"
services: services:
immich_server: immich_server:
image: immich-server-dev:1.5.0 image: immich-server-dev:1.6.0
build: build:
context: ../server context: ../server
dockerfile: Dockerfile dockerfile: Dockerfile
@@ -22,7 +22,7 @@ services:
- immich_network - immich_network
immich_microservices: immich_microservices:
image: immich-microservices-dev:1.5.0 image: immich-microservices-dev:1.6.0
build: build:
context: ../microservices context: ../microservices
dockerfile: Dockerfile dockerfile: Dockerfile

View File

@@ -2,7 +2,7 @@ version: "3.8"
services: services:
immich_server: immich_server:
image: immich-server:1.5.0 image: immich-server:1.6.0
build: build:
context: ../server context: ../server
dockerfile: Dockerfile dockerfile: Dockerfile
@@ -10,9 +10,7 @@ services:
expose: expose:
- "3000" - "3000"
volumes: volumes:
- ../server:/usr/src/app
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /usr/src/app/node_modules
env_file: env_file:
- .env - .env
environment: environment:
@@ -25,7 +23,7 @@ services:
restart: unless-stopped restart: unless-stopped
immich_microservices: immich_microservices:
image: immich-microservices:1.5.0 image: immich-microservices:1.6.0
build: build:
context: ../microservices context: ../microservices
dockerfile: Dockerfile dockerfile: Dockerfile
@@ -33,9 +31,7 @@ services:
expose: expose:
- "3001" - "3001"
volumes: volumes:
- ../microservices:/usr/src/app
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /usr/src/app/node_modules
env_file: env_file:
- .env - .env
environment: environment:
@@ -84,27 +80,6 @@ services:
depends_on: 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: 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: networks:
immich_network: immich_network:
volumes: volumes:

View File

@@ -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.

View File

@@ -19,7 +19,7 @@ platform :ios do
desc "iOS Beta" desc "iOS Beta"
lane :beta do lane :beta do
increment_version_number( increment_version_number(
version_number: "1.5.1" version_number: "1.6.0"
) )
increment_build_number( increment_build_number(
build_number: latest_testflight_build_number + 1, build_number: latest_testflight_build_number + 1,

View File

@@ -14,7 +14,7 @@ class LoginForm extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final usernameController = useTextEditingController(text: 'testuser@email.com'); final usernameController = useTextEditingController(text: 'testuser@email.com');
final passwordController = useTextEditingController(text: 'password'); final passwordController = useTextEditingController(text: 'password');
final serverEndpointController = useTextEditingController(text: 'http://192.168.1.216:2283'); final serverEndpointController = useTextEditingController(text: 'http://192.168.1.103:2283');
return Center( return Center(
child: ConstrainedBox( child: ConstrainedBox(

View File

@@ -37,24 +37,29 @@ class BackupNotifier extends StateNotifier<BackUpState> {
Ref? ref; Ref? ref;
final BackupService _backupService = BackupService(); final BackupService _backupService = BackupService();
final ServerInfoService _serverInfoService = ServerInfoService(); final ServerInfoService _serverInfoService = ServerInfoService();
final StreamController _onAssetBackupStreamCtrl = StreamController.broadcast(); final StreamController _onAssetBackupStreamCtrl =
StreamController.broadcast();
void getBackupInfo() async { void getBackupInfo() async {
_updateServerInfo(); _updateServerInfo();
List<AssetPathEntity> list = await PhotoManager.getAssetPathList(onlyAll: true, type: RequestType.common); List<AssetPathEntity> list = await PhotoManager.getAssetPathList(
onlyAll: true, type: RequestType.common);
List<String> didBackupAsset = await _backupService.getDeviceBackupAsset(); List<String> didBackupAsset = await _backupService.getDeviceBackupAsset();
if (list.isEmpty) { if (list.isEmpty) {
debugPrint("No Asset On Device"); debugPrint("No Asset On Device");
state = state.copyWith( state = state.copyWith(
backupProgress: BackUpProgressEnum.idle, totalAssetCount: 0, assetOnDatabase: didBackupAsset.length); backupProgress: BackUpProgressEnum.idle,
totalAssetCount: 0,
assetOnDatabase: didBackupAsset.length);
return; return;
} }
int totalAsset = list[0].assetCount; int totalAsset = list[0].assetCount;
state = state.copyWith(totalAssetCount: totalAsset, assetOnDatabase: didBackupAsset.length); state = state.copyWith(
totalAssetCount: totalAsset, assetOnDatabase: didBackupAsset.length);
} }
void startBackupProcess() async { void startBackupProcess() async {
@@ -67,8 +72,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
await PhotoManager.clearFileCache(); await PhotoManager.clearFileCache();
// await PhotoManager.presentLimited(); // await PhotoManager.presentLimited();
// Gather assets info // Gather assets info
List<AssetPathEntity> list = List<AssetPathEntity> list = await PhotoManager.getAssetPathList(
await PhotoManager.getAssetPathList(hasAll: true, onlyAll: true, type: RequestType.common); hasAll: true, onlyAll: true, type: RequestType.common);
// Get device assets info from database // Get device assets info from database
// Compare and find different assets that has not been backing up // Compare and find different assets that has not been backing up
@@ -78,14 +83,18 @@ class BackupNotifier extends StateNotifier<BackUpState> {
if (list.isEmpty) { if (list.isEmpty) {
debugPrint("No Asset On Device - Abort Backup Process"); debugPrint("No Asset On Device - Abort Backup Process");
state = state.copyWith( state = state.copyWith(
backupProgress: BackUpProgressEnum.idle, totalAssetCount: 0, assetOnDatabase: backupAsset.length); backupProgress: BackUpProgressEnum.idle,
totalAssetCount: 0,
assetOnDatabase: backupAsset.length);
return; return;
} }
int totalAsset = list[0].assetCount; int totalAsset = list[0].assetCount;
List<AssetEntity> currentAssets = await list[0].getAssetListRange(start: 0, end: totalAsset); List<AssetEntity> currentAssets =
await list[0].getAssetListRange(start: 0, end: totalAsset);
state = state.copyWith(totalAssetCount: totalAsset, assetOnDatabase: backupAsset.length); state = state.copyWith(
totalAssetCount: totalAsset, assetOnDatabase: backupAsset.length);
// Remove item that has already been backed up // Remove item that has already been backed up
for (var backupAssetId in backupAsset) { for (var backupAssetId in backupAsset) {
currentAssets.removeWhere((e) => e.id == backupAssetId); currentAssets.removeWhere((e) => e.id == backupAssetId);
@@ -97,9 +106,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
state = state.copyWith(backingUpAssetCount: currentAssets.length); state = state.copyWith(backingUpAssetCount: currentAssets.length);
// Perform Packup // Perform Backup
state = state.copyWith(cancelToken: CancelToken()); state = state.copyWith(cancelToken: CancelToken());
_backupService.backupAsset(currentAssets, state.cancelToken, _onAssetUploaded, _onUploadProgress); _backupService.backupAsset(currentAssets, state.cancelToken,
_onAssetUploaded, _onUploadProgress);
} else { } else {
PhotoManager.openSetting(); PhotoManager.openSetting();
} }
@@ -107,22 +117,26 @@ class BackupNotifier extends StateNotifier<BackUpState> {
void cancelBackup() { void cancelBackup() {
state.cancelToken.cancel('Cancel Backup'); state.cancelToken.cancel('Cancel Backup');
state = state.copyWith(backupProgress: BackUpProgressEnum.idle, progressInPercentage: 0.0); state = state.copyWith(
backupProgress: BackUpProgressEnum.idle, progressInPercentage: 0.0);
} }
void _onAssetUploaded(String deviceAssetId, String deviceId) { void _onAssetUploaded(String deviceAssetId, String deviceId) {
state = state = state.copyWith(
state.copyWith(backingUpAssetCount: state.backingUpAssetCount - 1, assetOnDatabase: state.assetOnDatabase + 1); backingUpAssetCount: state.backingUpAssetCount - 1,
assetOnDatabase: state.assetOnDatabase + 1);
if (state.backingUpAssetCount == 0) { if (state.backingUpAssetCount == 0) {
state = state.copyWith(backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0); state = state.copyWith(
backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0);
} }
_updateServerInfo(); _updateServerInfo();
} }
void _onUploadProgress(int sent, int total) { void _onUploadProgress(int sent, int total) {
state = state.copyWith(progressInPercentage: (sent.toDouble() / total.toDouble() * 100)); state = state.copyWith(
progressInPercentage: (sent.toDouble() / total.toDouble() * 100));
} }
void _updateServerInfo() async { void _updateServerInfo() async {
@@ -156,7 +170,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
} }
// Check if this device is enable backup by the user // Check if this device is enable backup by the user
if ((authState.deviceInfo.deviceId == authState.deviceId) && authState.deviceInfo.isAutoBackup) { if ((authState.deviceInfo.deviceId == authState.deviceId) &&
authState.deviceInfo.isAutoBackup) {
// check if backup is alreayd in process - then return // check if backup is alreayd in process - then return
if (state.backupProgress == BackUpProgressEnum.inProgress) { if (state.backupProgress == BackUpProgressEnum.inProgress) {
debugPrint("[resumeBackup] Backup is already in progress - abort"); debugPrint("[resumeBackup] Backup is already in progress - abort");
@@ -173,6 +188,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
} }
} }
final backupProvider = StateNotifierProvider<BackupNotifier, BackUpState>((ref) { final backupProvider =
StateNotifierProvider<BackupNotifier, BackUpState>((ref) {
return BackupNotifier(ref: ref); return BackupNotifier(ref: ref);
}); });

View File

@@ -112,7 +112,10 @@ class BackupService {
} }
} on DioError catch (e) { } on DioError catch (e) {
debugPrint("DioError backupAsset: ${e.response}"); debugPrint("DioError backupAsset: ${e.response}");
break; if (e.type == DioErrorType.cancel || e.type == DioErrorType.other) {
return;
}
continue;
} catch (e) { } catch (e) {
debugPrint("ERROR backupAsset: ${e.toString()}"); debugPrint("ERROR backupAsset: ${e.toString()}");
continue; continue;

View File

@@ -87,13 +87,16 @@ class BackupControllerPage extends HookConsumerWidget {
style: TextStyle(fontSize: 14), style: TextStyle(fontSize: 14),
) )
: Container(), : Container(),
OutlinedButton( Padding(
onPressed: () { padding: const EdgeInsets.only(top: 8.0),
isAutoBackup child: OutlinedButton(
? ref.watch(authenticationProvider.notifier).setAutoBackup(false) onPressed: () {
: ref.watch(authenticationProvider.notifier).setAutoBackup(true); isAutoBackup
}, ? ref.watch(authenticationProvider.notifier).setAutoBackup(false)
child: Text("Turn $backupBtnText Backup"), : ref.watch(authenticationProvider.notifier).setAutoBackup(true);
},
child: Text("Turn $backupBtnText Backup", style: const TextStyle(fontWeight: FontWeight.bold)),
),
) )
], ],
), ),

View File

@@ -35,6 +35,9 @@ class FileHelper {
case 'dng': case 'dng':
return {"type": "image", "subType": "dng"}; return {"type": "image", "subType": "dng"};
case 'webp':
return {"type": "image", "subType": "webp"};
default: default:
return {"type": "unsupport", "subType": "unsupport"}; return {"type": "unsupport", "subType": "unsupport"};
} }

View File

@@ -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: 1.5.1+9 version: 1.6.0+10
environment: environment:
sdk: ">=2.15.1 <3.0.0" sdk: ">=2.15.1 <3.0.0"

View File

@@ -12,7 +12,7 @@ export const multerConfig = {
export const multerOption: MulterOptions = { export const multerOption: MulterOptions = {
fileFilter: (req: Request, file: any, cb: any) => { fileFilter: (req: Request, file: any, cb: any) => {
if (file.mimetype.match(/\/(jpg|jpeg|png|gif|mp4|x-msvideo|quicktime|heic|heif|dng)$/)) { if (file.mimetype.match(/\/(jpg|jpeg|png|gif|mp4|x-msvideo|quicktime|heic|heif|dng|webp)$/)) {
cb(null, true); cb(null, true);
} else { } else {
cb(new HttpException(`Unsupported file type ${extname(file.originalname)}`, HttpStatus.BAD_REQUEST), false); cb(new HttpException(`Unsupported file type ${extname(file.originalname)}`, HttpStatus.BAD_REQUEST), false);

View File

@@ -3,7 +3,7 @@
export const serverVersion = { export const serverVersion = {
major: 1, major: 1,
minor: 5, minor: 6,
patch: 1, patch: 0,
build: 9, build: 10,
}; };