Compare commits
6 Commits
v1.5.0+8-d
...
v1.6.0_10-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7dff229db | ||
|
|
8e80825b4f | ||
|
|
a1481c1113 | ||
|
|
3bdcdef198 | ||
|
|
b69f6e0df7 | ||
|
|
be2794a372 |
64
.github/workflows/build_push_docker_latest.yml
vendored
Normal file
64
.github/workflows/build_push_docker_latest.yml
vendored
Normal 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
|
||||
42
.github/workflows/build_push_server_latest.yml
vendored
42
.github/workflows/build_push_server_latest.yml
vendored
@@ -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
|
||||
81
.github/workflows/build_push_server_release.yml
vendored
81
.github/workflows/build_push_server_release.yml
vendored
@@ -2,37 +2,94 @@ name: Build Server - Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
buildandpush:
|
||||
build_and_push_server_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: "main" # branch
|
||||
# https://github.com/docker/setup-qemu-action#usage
|
||||
ref: "main"
|
||||
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
|
||||
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
|
||||
|
||||
- name: Build and push immich-server release
|
||||
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:${{github.ref_name}}
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.previoustag.outputs.tag }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
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 }}
|
||||
@@ -59,7 +59,7 @@ This project is under heavy development, there will be continous functions, feat
|
||||
- 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
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
immich_server:
|
||||
image: immich-server-dev:1.5.0
|
||||
image: immich-server-dev:1.6.0
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
@@ -24,7 +24,7 @@ services:
|
||||
- immich_network
|
||||
|
||||
immich_microservices:
|
||||
image: immich-microservices-dev:1.5.0
|
||||
image: immich-microservices-dev:1.6.0
|
||||
build:
|
||||
context: ../microservices
|
||||
dockerfile: Dockerfile
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
immich_server:
|
||||
image: immich-server-dev:1.5.0
|
||||
image: immich-server-dev:1.6.0
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
@@ -22,7 +22,7 @@ services:
|
||||
- immich_network
|
||||
|
||||
immich_microservices:
|
||||
image: immich-microservices-dev:1.5.0
|
||||
image: immich-microservices-dev:1.6.0
|
||||
build:
|
||||
context: ../microservices
|
||||
dockerfile: Dockerfile
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
immich_server:
|
||||
image: immich-server:1.5.0
|
||||
image: immich-server:1.6.0
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
@@ -10,9 +10,7 @@ services:
|
||||
expose:
|
||||
- "3000"
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
@@ -25,7 +23,7 @@ services:
|
||||
restart: unless-stopped
|
||||
|
||||
immich_microservices:
|
||||
image: immich-microservices:1.5.0
|
||||
image: immich-microservices:1.6.0
|
||||
build:
|
||||
context: ../microservices
|
||||
dockerfile: Dockerfile
|
||||
@@ -33,9 +31,7 @@ services:
|
||||
expose:
|
||||
- "3001"
|
||||
volumes:
|
||||
- ../microservices:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
@@ -84,27 +80,6 @@ services:
|
||||
depends_on:
|
||||
- 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:
|
||||
immich_network:
|
||||
volumes:
|
||||
|
||||
@@ -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 @@
|
||||
* Added inline font, remove google-font dependency in pubspec.
|
||||
BIN
mobile/fonts/SnowburstOne.ttf
Normal file
BIN
mobile/fonts/SnowburstOne.ttf
Normal file
Binary file not shown.
BIN
mobile/fonts/WorkSans-Italic.ttf
Normal file
BIN
mobile/fonts/WorkSans-Italic.ttf
Normal file
Binary file not shown.
BIN
mobile/fonts/WorkSans.ttf
Normal file
BIN
mobile/fonts/WorkSans.ttf
Normal file
Binary file not shown.
@@ -19,11 +19,11 @@ platform :ios do
|
||||
desc "iOS Beta"
|
||||
lane :beta do
|
||||
increment_version_number(
|
||||
version_number: "1.5.0"
|
||||
version_number: "1.6.0"
|
||||
)
|
||||
increment_build_number(
|
||||
build_number: latest_testflight_build_number + 1,
|
||||
)
|
||||
increment_build_number({
|
||||
build_number: 0
|
||||
})
|
||||
build_app(scheme: "Runner",
|
||||
workspace: "Runner.xcworkspace",
|
||||
xcargs: "-allowProvisioningUpdates")
|
||||
|
||||
@@ -10,7 +10,6 @@ import 'package:immich_mobile/shared/providers/backup.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/websocket.provider.dart';
|
||||
import 'constants/hive_box.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
void main() async {
|
||||
await Hive.initFlutter();
|
||||
@@ -94,10 +93,11 @@ class _ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserv
|
||||
theme: ThemeData(
|
||||
brightness: Brightness.light,
|
||||
primarySwatch: Colors.indigo,
|
||||
textTheme: GoogleFonts.workSansTextTheme(
|
||||
Theme.of(context).textTheme.apply(fontSizeFactor: 1.0),
|
||||
),
|
||||
snackBarTheme: SnackBarThemeData(contentTextStyle: TextStyle(fontFamily: GoogleFonts.workSans().fontFamily)),
|
||||
// textTheme: GoogleFonts.workSansTextTheme(
|
||||
// Theme.of(context).textTheme.apply(fontSizeFactor: 1.0),
|
||||
// ),
|
||||
fontFamily: 'WorkSans',
|
||||
snackBarTheme: const SnackBarThemeData(contentTextStyle: TextStyle(fontFamily: 'WorkSans')),
|
||||
scaffoldBackgroundColor: const Color(0xFFf6f8fe),
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: Colors.white,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:badges/badges.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
|
||||
@@ -79,12 +78,11 @@ class ImmichSliverAppBar extends ConsumerWidget {
|
||||
),
|
||||
title: Text(
|
||||
'IMMICH',
|
||||
style: GoogleFonts.snowburstOne(
|
||||
textStyle: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
style: TextStyle(
|
||||
fontFamily: 'SnowburstOne',
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
@@ -121,8 +119,12 @@ class ImmichSliverAppBar extends ConsumerWidget {
|
||||
),
|
||||
child: const Icon(Icons.backup_rounded)),
|
||||
tooltip: 'Backup Controller',
|
||||
onPressed: () {
|
||||
AutoRouter.of(context).push(const BackupControllerRoute());
|
||||
onPressed: () async {
|
||||
var onPop = await AutoRouter.of(context).push(const BackupControllerRoute());
|
||||
|
||||
if (onPop != null && onPop == true) {
|
||||
onPopBack!();
|
||||
}
|
||||
},
|
||||
),
|
||||
_backupState.backupProgress == BackUpProgressEnum.inProgress
|
||||
|
||||
@@ -33,6 +33,10 @@ class HomePage extends HookConsumerWidget {
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
void reloadAllAsset() {
|
||||
ref.read(assetProvider.notifier).getAllAsset();
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
if (assetGroupByDateTime.isNotEmpty) {
|
||||
int? lastMonth;
|
||||
@@ -86,7 +90,9 @@ class HomePage extends HookConsumerWidget {
|
||||
child: null,
|
||||
),
|
||||
)
|
||||
: const ImmichSliverAppBar(),
|
||||
: ImmichSliverAppBar(
|
||||
onPopBack: reloadAllAsset,
|
||||
),
|
||||
duration: const Duration(milliseconds: 350),
|
||||
),
|
||||
..._imageGridGroup
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/home/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
@@ -15,7 +14,7 @@ class LoginForm extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final usernameController = useTextEditingController(text: 'testuser@email.com');
|
||||
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(
|
||||
child: ConstrainedBox(
|
||||
@@ -33,9 +32,12 @@ class LoginForm extends HookConsumerWidget {
|
||||
),
|
||||
Text(
|
||||
'IMMICH',
|
||||
style: GoogleFonts.snowburstOne(
|
||||
textStyle:
|
||||
TextStyle(fontWeight: FontWeight.bold, fontSize: 48, color: Theme.of(context).primaryColor)),
|
||||
style: TextStyle(
|
||||
fontFamily: 'SnowburstOne',
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 48,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
EmailInput(controller: usernameController),
|
||||
PasswordInput(controller: passwordController),
|
||||
|
||||
@@ -37,24 +37,29 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
Ref? ref;
|
||||
final BackupService _backupService = BackupService();
|
||||
final ServerInfoService _serverInfoService = ServerInfoService();
|
||||
final StreamController _onAssetBackupStreamCtrl = StreamController.broadcast();
|
||||
final StreamController _onAssetBackupStreamCtrl =
|
||||
StreamController.broadcast();
|
||||
|
||||
void getBackupInfo() async {
|
||||
_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();
|
||||
|
||||
if (list.isEmpty) {
|
||||
debugPrint("No Asset On Device");
|
||||
state = state.copyWith(
|
||||
backupProgress: BackUpProgressEnum.idle, totalAssetCount: 0, assetOnDatabase: didBackupAsset.length);
|
||||
backupProgress: BackUpProgressEnum.idle,
|
||||
totalAssetCount: 0,
|
||||
assetOnDatabase: didBackupAsset.length);
|
||||
return;
|
||||
}
|
||||
|
||||
int totalAsset = list[0].assetCount;
|
||||
|
||||
state = state.copyWith(totalAssetCount: totalAsset, assetOnDatabase: didBackupAsset.length);
|
||||
state = state.copyWith(
|
||||
totalAssetCount: totalAsset, assetOnDatabase: didBackupAsset.length);
|
||||
}
|
||||
|
||||
void startBackupProcess() async {
|
||||
@@ -67,8 +72,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
await PhotoManager.clearFileCache();
|
||||
// await PhotoManager.presentLimited();
|
||||
// Gather assets info
|
||||
List<AssetPathEntity> list =
|
||||
await PhotoManager.getAssetPathList(hasAll: true, onlyAll: true, type: RequestType.common);
|
||||
List<AssetPathEntity> list = await PhotoManager.getAssetPathList(
|
||||
hasAll: true, onlyAll: true, type: RequestType.common);
|
||||
|
||||
// Get device assets info from database
|
||||
// Compare and find different assets that has not been backing up
|
||||
@@ -78,14 +83,18 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
if (list.isEmpty) {
|
||||
debugPrint("No Asset On Device - Abort Backup Process");
|
||||
state = state.copyWith(
|
||||
backupProgress: BackUpProgressEnum.idle, totalAssetCount: 0, assetOnDatabase: backupAsset.length);
|
||||
backupProgress: BackUpProgressEnum.idle,
|
||||
totalAssetCount: 0,
|
||||
assetOnDatabase: backupAsset.length);
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
for (var backupAssetId in backupAsset) {
|
||||
currentAssets.removeWhere((e) => e.id == backupAssetId);
|
||||
@@ -97,9 +106,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
|
||||
state = state.copyWith(backingUpAssetCount: currentAssets.length);
|
||||
|
||||
// Perform Packup
|
||||
// Perform Backup
|
||||
state = state.copyWith(cancelToken: CancelToken());
|
||||
_backupService.backupAsset(currentAssets, state.cancelToken, _onAssetUploaded, _onUploadProgress);
|
||||
_backupService.backupAsset(currentAssets, state.cancelToken,
|
||||
_onAssetUploaded, _onUploadProgress);
|
||||
} else {
|
||||
PhotoManager.openSetting();
|
||||
}
|
||||
@@ -107,22 +117,26 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
|
||||
void cancelBackup() {
|
||||
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) {
|
||||
state =
|
||||
state.copyWith(backingUpAssetCount: state.backingUpAssetCount - 1, assetOnDatabase: state.assetOnDatabase + 1);
|
||||
state = state.copyWith(
|
||||
backingUpAssetCount: state.backingUpAssetCount - 1,
|
||||
assetOnDatabase: state.assetOnDatabase + 1);
|
||||
|
||||
if (state.backingUpAssetCount == 0) {
|
||||
state = state.copyWith(backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0);
|
||||
state = state.copyWith(
|
||||
backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0);
|
||||
}
|
||||
|
||||
_updateServerInfo();
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -156,7 +170,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
}
|
||||
|
||||
// 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
|
||||
if (state.backupProgress == BackUpProgressEnum.inProgress) {
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -106,6 +106,20 @@ class WebsocketNotifier extends StateNotifier<WebscoketState> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopListenToEvent(String eventName) {
|
||||
debugPrint("[Websocket] Stop listening to event $eventName");
|
||||
state.socket?.off(eventName);
|
||||
}
|
||||
|
||||
listenUploadEvent() {
|
||||
debugPrint("[Websocket] Start listening to event on_upload_success");
|
||||
state.socket?.on('on_upload_success', (data) {
|
||||
var jsonString = jsonDecode(data.toString());
|
||||
ImmichAsset newAsset = ImmichAsset.fromMap(jsonString);
|
||||
ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final websocketProvider = StateNotifierProvider<WebsocketNotifier, WebscoketState>((ref) {
|
||||
|
||||
@@ -112,7 +112,10 @@ class BackupService {
|
||||
}
|
||||
} on DioError catch (e) {
|
||||
debugPrint("DioError backupAsset: ${e.response}");
|
||||
break;
|
||||
if (e.type == DioErrorType.cancel || e.type == DioErrorType.other) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
} catch (e) {
|
||||
debugPrint("ERROR backupAsset: ${e.toString()}");
|
||||
continue;
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:immich_mobile/modules/login/models/authentication_state.model.da
|
||||
import 'package:immich_mobile/shared/models/backup_state.model.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/backup.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/websocket.provider.dart';
|
||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||
|
||||
class BackupControllerPage extends HookConsumerWidget {
|
||||
@@ -23,6 +24,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
ref.read(backupProvider.notifier).getBackupInfo();
|
||||
}
|
||||
|
||||
ref.watch(websocketProvider.notifier).stopListenToEvent('on_upload_success');
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
@@ -85,13 +87,16 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
style: TextStyle(fontSize: 14),
|
||||
)
|
||||
: Container(),
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
isAutoBackup
|
||||
? ref.watch(authenticationProvider.notifier).setAutoBackup(false)
|
||||
: ref.watch(authenticationProvider.notifier).setAutoBackup(true);
|
||||
},
|
||||
child: Text("Turn $backupBtnText Backup"),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
isAutoBackup
|
||||
? ref.watch(authenticationProvider.notifier).setAutoBackup(false)
|
||||
: ref.watch(authenticationProvider.notifier).setAutoBackup(true);
|
||||
},
|
||||
child: Text("Turn $backupBtnText Backup", style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
@@ -107,6 +112,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
),
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
ref.watch(websocketProvider.notifier).listenUploadEvent();
|
||||
AutoRouter.of(context).pop(true);
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back_ios_rounded)),
|
||||
|
||||
@@ -35,6 +35,9 @@ class FileHelper {
|
||||
case 'dng':
|
||||
return {"type": "image", "subType": "dng"};
|
||||
|
||||
case 'webp':
|
||||
return {"type": "image", "subType": "webp"};
|
||||
|
||||
default:
|
||||
return {"type": "unsupport", "subType": "unsupport"};
|
||||
}
|
||||
|
||||
@@ -373,13 +373,6 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
google_fonts:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_fonts
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -2,7 +2,7 @@ name: immich_mobile
|
||||
description: Immich - selfhosted backup media file on mobile phone
|
||||
|
||||
publish_to: "none"
|
||||
version: 1.5.0+8
|
||||
version: 1.6.0+10
|
||||
|
||||
environment:
|
||||
sdk: ">=2.15.1 <3.0.0"
|
||||
@@ -18,7 +18,7 @@ dependencies:
|
||||
hive_flutter:
|
||||
dio: ^4.0.4
|
||||
cached_network_image: ^3.2.0
|
||||
google_fonts: ^2.2.0
|
||||
# google_fonts: ^2.2.0
|
||||
percent_indicator: ^3.4.0
|
||||
intl: ^0.17.0
|
||||
auto_route: ^3.2.2
|
||||
@@ -50,6 +50,15 @@ flutter:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- assets/
|
||||
fonts:
|
||||
- family: WorkSans
|
||||
fonts:
|
||||
- asset: fonts/WorkSans.ttf
|
||||
- asset: fonts/WorkSans-Italic.ttf
|
||||
style: italic
|
||||
- family: SnowburstOne
|
||||
fonts:
|
||||
- asset: fonts/SnowburstOne.ttf
|
||||
|
||||
flutter_icons:
|
||||
image_path_android: "assets/immich-logo-no-outline.png"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "immich",
|
||||
"version": "1.3.2",
|
||||
"version": "1.5.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
|
||||
@@ -115,18 +115,8 @@ export class AssetController {
|
||||
return this.assetService.searchAsset(authUser, searchAssetDto);
|
||||
}
|
||||
|
||||
@Get('/new')
|
||||
async getNewAssets(@GetAuthUser() authUser: AuthUserDto, @Query(ValidationPipe) query: GetNewAssetQueryDto) {
|
||||
return await this.assetService.getNewAssets(authUser, query.latestDate);
|
||||
}
|
||||
|
||||
@Get('/all')
|
||||
async getAllAssets(@GetAuthUser() authUser: AuthUserDto, @Query(ValidationPipe) query: GetAllAssetQueryDto) {
|
||||
return await this.assetService.getAllAssets(authUser, query);
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
async getAllAssetsNoPagination(@GetAuthUser() authUser: AuthUserDto) {
|
||||
async getAllAssets(@GetAuthUser() authUser: AuthUserDto) {
|
||||
return await this.assetService.getAllAssetsNoPagination(authUser);
|
||||
}
|
||||
|
||||
@@ -137,7 +127,7 @@ export class AssetController {
|
||||
|
||||
@Get('/assetById/:assetId')
|
||||
async getAssetById(@GetAuthUser() authUser: AuthUserDto, @Param('assetId') assetId) {
|
||||
return this.assetService.getAssetById(authUser, assetId);
|
||||
return await this.assetService.getAssetById(authUser, assetId);
|
||||
}
|
||||
|
||||
@Delete('/')
|
||||
|
||||
@@ -76,42 +76,6 @@ export class AssetService {
|
||||
}
|
||||
}
|
||||
|
||||
public async getAllAssets(authUser: AuthUserDto, query: GetAllAssetQueryDto): Promise<GetAllAssetReponseDto> {
|
||||
try {
|
||||
const assets = await this.assetRepository
|
||||
.createQueryBuilder('a')
|
||||
.where('a."userId" = :userId', { userId: authUser.id })
|
||||
.andWhere('a."createdAt" < :lastQueryCreatedAt', {
|
||||
lastQueryCreatedAt: query.nextPageKey || new Date().toISOString(),
|
||||
})
|
||||
.orderBy('a."createdAt"::date', 'DESC')
|
||||
.take(5000)
|
||||
.getMany();
|
||||
|
||||
if (assets.length > 0) {
|
||||
const data = _.groupBy(assets, (a) => new Date(a.createdAt).toISOString().slice(0, 10));
|
||||
const formattedData = [];
|
||||
Object.keys(data).forEach((v) => formattedData.push({ date: v, assets: data[v] }));
|
||||
|
||||
const response = new GetAllAssetReponseDto();
|
||||
response.count = assets.length;
|
||||
response.data = formattedData;
|
||||
response.nextPageKey = assets[assets.length - 1].createdAt;
|
||||
|
||||
return response;
|
||||
} else {
|
||||
const response = new GetAllAssetReponseDto();
|
||||
response.count = 0;
|
||||
response.data = [];
|
||||
response.nextPageKey = 'null';
|
||||
|
||||
return response;
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.error(e, 'getAllAssets');
|
||||
}
|
||||
}
|
||||
|
||||
public async findOne(authUser: AuthUserDto, deviceId: string, assetId: string): Promise<AssetEntity> {
|
||||
const rows = await this.assetRepository.query(
|
||||
'SELECT * FROM assets a WHERE a."deviceAssetId" = $1 AND a."userId" = $2 AND a."deviceId" = $3',
|
||||
@@ -125,18 +89,6 @@ export class AssetService {
|
||||
return rows[0] as AssetEntity;
|
||||
}
|
||||
|
||||
public async getNewAssets(authUser: AuthUserDto, latestDate: string) {
|
||||
return await this.assetRepository.find({
|
||||
where: {
|
||||
userId: authUser.id,
|
||||
createdAt: MoreThan(latestDate),
|
||||
},
|
||||
order: {
|
||||
createdAt: 'ASC', // ASC order to add existed asset the latest group first before creating a new date group.
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public async getAssetById(authUser: AuthUserDto, assetId: string) {
|
||||
return await this.assetRepository.findOne({
|
||||
where: {
|
||||
|
||||
@@ -12,7 +12,7 @@ export const multerConfig = {
|
||||
|
||||
export const multerOption: MulterOptions = {
|
||||
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);
|
||||
} else {
|
||||
cb(new HttpException(`Unsupported file type ${extname(file.originalname)}`, HttpStatus.BAD_REQUEST), false);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
export const serverVersion = {
|
||||
major: 1,
|
||||
minor: 5,
|
||||
minor: 6,
|
||||
patch: 0,
|
||||
build: 8,
|
||||
build: 10,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user