Compare commits
32 Commits
v1.35.0_54
...
v1.36.0_55
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37a4f4a39f | ||
|
|
9d2c30298e | ||
|
|
6f5d60fb62 | ||
|
|
41ffa0c015 | ||
|
|
b3e51cc849 | ||
|
|
e01e4e6530 | ||
|
|
6ed072f67b | ||
|
|
8bc64be77b | ||
|
|
83e2cabbcc | ||
|
|
7de7619fd1 | ||
|
|
afae5fd972 | ||
|
|
70cd313082 | ||
|
|
e799f35dd2 | ||
|
|
1db255fd3e | ||
|
|
909e4820d6 | ||
|
|
4727671c79 | ||
|
|
f2f255e6e6 | ||
|
|
b5d75e2016 | ||
|
|
d3c35ec9c5 | ||
|
|
d476656789 | ||
|
|
8d0ff974e1 | ||
|
|
33ded2a174 | ||
|
|
277af33ab0 | ||
|
|
2e4c005ad9 | ||
|
|
739bed737e | ||
|
|
a1a7e6ac06 | ||
|
|
c3348bd068 | ||
|
|
cc61729f01 | ||
|
|
b457bfbd4e | ||
|
|
1877834fd1 | ||
|
|
afdfd1863f | ||
|
|
f6aba0f9ec |
32
.github/workflows/build_push_docker_latest.yml
vendored
32
.github/workflows/build_push_docker_latest.yml
vendored
@@ -26,6 +26,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push Immich Mono Repo
|
- name: Build and push Immich Mono Repo
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -33,8 +39,11 @@ jobs:
|
|||||||
file: ./server/Dockerfile
|
file: ./server/Dockerfile
|
||||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-server:latest
|
altran1502/immich-server:latest
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-server:latest
|
||||||
|
|
||||||
build_and_push_machine_learning_latest:
|
build_and_push_machine_learning_latest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -54,6 +63,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and Push Machine Learning
|
- name: Build and Push Machine Learning
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -61,8 +76,11 @@ jobs:
|
|||||||
file: ./machine-learning/Dockerfile
|
file: ./machine-learning/Dockerfile
|
||||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-machine-learning:latest
|
altran1502/immich-machine-learning:latest
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-machine-learning:latest
|
||||||
|
|
||||||
build_and_push_web_latest:
|
build_and_push_web_latest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -81,6 +99,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and Push Web
|
- name: Build and Push Web
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -91,6 +115,7 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-web:latest
|
altran1502/immich-web:latest
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-web:latest
|
||||||
|
|
||||||
build_and_push_nginx_latest:
|
build_and_push_nginx_latest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -109,6 +134,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and Push Proxy
|
- name: Build and Push Proxy
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -118,3 +149,4 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-proxy:latest
|
altran1502/immich-proxy:latest
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-proxy:latest
|
||||||
|
|||||||
40
.github/workflows/build_push_docker_staging.yml
vendored
40
.github/workflows/build_push_docker_staging.yml
vendored
@@ -27,6 +27,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
if: ${{ github.repository == 'immich-app/immich' }}
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push Immich Mono Repo
|
- name: Build and push Immich Mono Repo
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -34,9 +41,13 @@ jobs:
|
|||||||
file: ./server/Dockerfile
|
file: ./server/Dockerfile
|
||||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||||
push: ${{ github.event_name == 'pull_request' && github.repository == 'immich-app/immich' }}
|
push: ${{ github.event_name == 'pull_request' && github.repository == 'immich-app/immich' }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-server:staging
|
altran1502/immich-server:staging
|
||||||
altran1502/immich-server:${{ github.event.pull_request.number }}
|
altran1502/immich-server:${{ github.event.pull_request.number }}
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-server:staging
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-server:${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
build_and_push_machine_learning_staging:
|
build_and_push_machine_learning_staging:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -57,6 +68,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
if: ${{ github.repository == 'immich-app/immich' }}
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and Push Machine Learning
|
- name: Build and Push Machine Learning
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -64,9 +82,13 @@ jobs:
|
|||||||
file: ./machine-learning/Dockerfile
|
file: ./machine-learning/Dockerfile
|
||||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||||
push: ${{ github.event_name == 'pull_request' && github.repository == 'immich-app/immich' }}
|
push: ${{ github.event_name == 'pull_request' && github.repository == 'immich-app/immich' }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-machine-learning:staging
|
altran1502/immich-machine-learning:staging
|
||||||
altran1502/immich-machine-learning:${{ github.event.pull_request.number }}
|
altran1502/immich-machine-learning:${{ github.event.pull_request.number }}
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-machine-learning:staging
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-machine-learning:${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
build_and_push_web_staging:
|
build_and_push_web_staging:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -86,6 +108,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
if: ${{ github.repository == 'immich-app/immich' }}
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and Push Web
|
- name: Build and Push Web
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -97,6 +126,8 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-web:staging
|
altran1502/immich-web:staging
|
||||||
altran1502/immich-web:${{ github.event.pull_request.number }}
|
altran1502/immich-web:${{ github.event.pull_request.number }}
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-web:staging
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-web:${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
build_and_push_nginx_staging:
|
build_and_push_nginx_staging:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -116,6 +147,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
if: ${{ github.repository == 'immich-app/immich' }}
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and Push Proxy
|
- name: Build and Push Proxy
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -126,3 +164,5 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-proxy:staging
|
altran1502/immich-proxy:staging
|
||||||
altran1502/immich-proxy:${{ github.event.pull_request.number }}
|
altran1502/immich-proxy:${{ github.event.pull_request.number }}
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-proxy:staging
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-proxy:${{ github.event.pull_request.number }}
|
||||||
|
|||||||
61
.github/workflows/build_push_server_release.yml
vendored
61
.github/workflows/build_push_server_release.yml
vendored
@@ -12,12 +12,12 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: "main"
|
ref: 'main'
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: "Get Previous tag"
|
- name: 'Get Previous tag'
|
||||||
id: previoustag
|
id: previoustag
|
||||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
uses: 'WyriHaximus/github-action-get-previous-tag@v1'
|
||||||
with:
|
with:
|
||||||
fallback: latest
|
fallback: latest
|
||||||
|
|
||||||
@@ -34,6 +34,13 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push immich-server release
|
- name: Build and push immich-server release
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -41,9 +48,13 @@ jobs:
|
|||||||
file: ./server/Dockerfile
|
file: ./server/Dockerfile
|
||||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-server:${{ steps.previoustag.outputs.tag }}
|
altran1502/immich-server:${{ steps.previoustag.outputs.tag }}
|
||||||
altran1502/immich-server:release
|
altran1502/immich-server:release
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-server:${{ steps.previoustag.outputs.tag }}
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-server:release
|
||||||
|
|
||||||
build_and_push_machine_learning_release:
|
build_and_push_machine_learning_release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -52,9 +63,9 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: "Get Previous tag"
|
- name: 'Get Previous tag'
|
||||||
id: previoustag
|
id: previoustag
|
||||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
uses: 'WyriHaximus/github-action-get-previous-tag@v1'
|
||||||
with:
|
with:
|
||||||
fallback: latest
|
fallback: latest
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
@@ -67,6 +78,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and Push Machine Learning
|
- name: Build and Push Machine Learning
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -74,9 +91,13 @@ jobs:
|
|||||||
file: ./machine-learning/Dockerfile
|
file: ./machine-learning/Dockerfile
|
||||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-machine-learning:${{ steps.previoustag.outputs.tag }}
|
altran1502/immich-machine-learning:${{ steps.previoustag.outputs.tag }}
|
||||||
altran1502/immich-machine-learning:release
|
altran1502/immich-machine-learning:release
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-machine-learning:${{ steps.previoustag.outputs.tag }}
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-machine-learning:release
|
||||||
|
|
||||||
build_and_push_web_release:
|
build_and_push_web_release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -84,12 +105,12 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: "main"
|
ref: 'main'
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: "Get Previous tag"
|
- name: 'Get Previous tag'
|
||||||
id: previoustag
|
id: previoustag
|
||||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
uses: 'WyriHaximus/github-action-get-previous-tag@v1'
|
||||||
with:
|
with:
|
||||||
fallback: latest
|
fallback: latest
|
||||||
|
|
||||||
@@ -106,6 +127,13 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push immich-web release
|
- name: Build and push immich-web release
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -117,6 +145,8 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-web:${{ steps.previoustag.outputs.tag }}
|
altran1502/immich-web:${{ steps.previoustag.outputs.tag }}
|
||||||
altran1502/immich-web:release
|
altran1502/immich-web:release
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-web:${{ steps.previoustag.outputs.tag }}
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-web:release
|
||||||
|
|
||||||
build_and_push_nginx_release:
|
build_and_push_nginx_release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -124,12 +154,12 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: "main"
|
ref: 'main'
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: "Get Previous tag"
|
- name: 'Get Previous tag'
|
||||||
id: previoustag
|
id: previoustag
|
||||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
uses: 'WyriHaximus/github-action-get-previous-tag@v1'
|
||||||
with:
|
with:
|
||||||
fallback: latest
|
fallback: latest
|
||||||
|
|
||||||
@@ -146,6 +176,13 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push immich-proxy release
|
- name: Build and push immich-proxy release
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
@@ -156,3 +193,5 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
altran1502/immich-proxy:release
|
altran1502/immich-proxy:release
|
||||||
altran1502/immich-proxy:${{ steps.previoustag.outputs.tag }}
|
altran1502/immich-proxy:${{ steps.previoustag.outputs.tag }}
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-proxy:${{ steps.previoustag.outputs.tag }}
|
||||||
|
ghcr.io/${{ github.repository_owner }}/immich-proxy:release
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
- ⚠️ The project is under **very active** development.
|
- ⚠️ The project is under **very active** development.
|
||||||
- ⚠️ Expect bugs and breaking changes.
|
- ⚠️ Expect bugs and breaking changes.
|
||||||
- ⚠️ **Do not use as a single source to store of your photos and videos!**
|
- ⚠️ **Do not use the app as the only way to store your photos and videos!**
|
||||||
|
|
||||||
## Content
|
## Content
|
||||||
|
|
||||||
@@ -35,6 +35,10 @@
|
|||||||
- [Support The Project](#support-the-project)
|
- [Support The Project](#support-the-project)
|
||||||
- [Known Issues](#known-issues)
|
- [Known Issues](#known-issues)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
You can find the main documentation, including installation guides, at https://immich.app/.
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
You can access the web demo at https://demo.immich.app
|
You can access the web demo at https://demo.immich.app
|
||||||
|
|||||||
@@ -67,3 +67,14 @@ JWT_SECRET=
|
|||||||
# For example PUBLIC_LOGIN_PAGE_MESSAGE="This is a demo instance of Immich.<br><br>Email: <i>demo@demo.de</i><br>Password: <i>demo</i>"
|
# For example PUBLIC_LOGIN_PAGE_MESSAGE="This is a demo instance of Immich.<br><br>Email: <i>demo@demo.de</i><br>Password: <i>demo</i>"
|
||||||
|
|
||||||
PUBLIC_LOGIN_PAGE_MESSAGE=
|
PUBLIC_LOGIN_PAGE_MESSAGE=
|
||||||
|
|
||||||
|
####################################################################################
|
||||||
|
# Alternative Service Addresses - Optional
|
||||||
|
####################################################################################
|
||||||
|
|
||||||
|
# This is an advanced feature for users who may be running their immich services on different hosts. It will not change which address or port that services bind to within their containers, but it will change where other services look for their peers.
|
||||||
|
# Note: immich-microservices is bound to 3002, but no references are made
|
||||||
|
|
||||||
|
# IMMICH_WEB_URL=http://immich-web:3000
|
||||||
|
# IMMICH_SERVER_URL=http://immich-server:3001
|
||||||
|
# IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ services:
|
|||||||
command: npm run dev --host
|
command: npm run dev --host
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
environment:
|
||||||
|
# Rename these values for svelte public interface
|
||||||
|
- PUBLIC_IMMICH_SERVER_URL=${IMMICH_SERVER_URL}
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
- 24678:24678
|
- 24678:24678
|
||||||
@@ -100,6 +103,10 @@ services:
|
|||||||
immich-proxy:
|
immich-proxy:
|
||||||
container_name: immich_proxy
|
container_name: immich_proxy
|
||||||
image: immich-proxy-dev:latest
|
image: immich-proxy-dev:latest
|
||||||
|
environment:
|
||||||
|
# Make sure these values get passed through from the env file
|
||||||
|
- IMMICH_SERVER_URL
|
||||||
|
- IMMICH_WEB_URL
|
||||||
build:
|
build:
|
||||||
context: ../nginx
|
context: ../nginx
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ services:
|
|||||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
environment:
|
||||||
|
# Rename these values for svelte public interface
|
||||||
|
- PUBLIC_IMMICH_SERVER_URL=${IMMICH_SERVER_URL}
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
@@ -71,6 +74,10 @@ services:
|
|||||||
immich-proxy:
|
immich-proxy:
|
||||||
container_name: immich_proxy
|
container_name: immich_proxy
|
||||||
image: altran1502/immich-proxy:staging
|
image: altran1502/immich-proxy:staging
|
||||||
|
environment:
|
||||||
|
# Make sure these values get passed through from the env file
|
||||||
|
- IMMICH_SERVER_URL
|
||||||
|
- IMMICH_WEB_URL
|
||||||
ports:
|
ports:
|
||||||
- 2283:8080
|
- 2283:8080
|
||||||
logging:
|
logging:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
version: "3.8"
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
immich-server-test:
|
immich-server-test:
|
||||||
@@ -9,7 +9,7 @@ services:
|
|||||||
target: builder
|
target: builder
|
||||||
command: npm run test:e2e
|
command: npm run test:e2e
|
||||||
expose:
|
expose:
|
||||||
- "3000"
|
- '3000'
|
||||||
volumes:
|
volumes:
|
||||||
- ../server:/usr/src/app
|
- ../server:/usr/src/app
|
||||||
- /usr/src/app/node_modules
|
- /usr/src/app/node_modules
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ services:
|
|||||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
environment:
|
||||||
|
# Rename these values for svelte public interface
|
||||||
|
- PUBLIC_IMMICH_SERVER_URL=${IMMICH_SERVER_URL}
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
@@ -71,6 +74,10 @@ services:
|
|||||||
immich-proxy:
|
immich-proxy:
|
||||||
container_name: immich_proxy
|
container_name: immich_proxy
|
||||||
image: altran1502/immich-proxy:release
|
image: altran1502/immich-proxy:release
|
||||||
|
environment:
|
||||||
|
# Make sure these values get passed through from the env file
|
||||||
|
- IMMICH_SERVER_URL
|
||||||
|
- IMMICH_WEB_URL
|
||||||
ports:
|
ports:
|
||||||
- 2283:8080
|
- 2283:8080
|
||||||
logging:
|
logging:
|
||||||
|
|||||||
1
docs/.gitignore
vendored
1
docs/.gitignore
vendored
@@ -18,3 +18,4 @@
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
yarn.lock
|
||||||
@@ -20,14 +20,23 @@ This environment includes the following services:
|
|||||||
|
|
||||||
All the services are packaged to run as with single Docker Compose command.
|
All the services are packaged to run as with single Docker Compose command.
|
||||||
|
|
||||||
After cloning the project, from the root directory run
|
### Instructions
|
||||||
|
|
||||||
|
1. Clone the project repo.
|
||||||
|
2. Run `cp docker/.env.example docker/.env`.
|
||||||
|
3. Edit `docker/.env` to provide values for the required variables `UPLOAD_LOCATION` and `JWT_SECRET`.
|
||||||
|
4. From the root directory, run:
|
||||||
|
|
||||||
```bash title="Start development server"
|
```bash title="Start development server"
|
||||||
make dev # required Makefile installed on the system.
|
make dev # required Makefile installed on the system.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
5. Access the dev instance in your browser at http://localhost:2283, or connect via the mobile app.
|
||||||
|
|
||||||
All the services will be started with hot-reloading enabled for a quick feedback loop.
|
All the services will be started with hot-reloading enabled for a quick feedback loop.
|
||||||
|
|
||||||
|
You can access the web from `http://your-machine-ip:2283` or `http://localhost:2283` and access the server from the mobile app at `http://your-machine-ip:2283/api`
|
||||||
|
|
||||||
### Mobile app
|
### Mobile app
|
||||||
|
|
||||||
The mobile app `(/mobile)` will required Flutter toolchain to be installed on your system.
|
The mobile app `(/mobile)` will required Flutter toolchain to be installed on your system.
|
||||||
@@ -80,3 +89,15 @@ OpenAPI is used to generate the client (Typescript, Dart) SDK. `openapi-generato
|
|||||||
npm run api:generate # Run from the `server` directory
|
npm run api:generate # Run from the `server` directory
|
||||||
```
|
```
|
||||||
You can find the generated client SDK in the `web/src/api` for Typescript SDK and `mobile/openapi` for Dart SDK.
|
You can find the generated client SDK in the `web/src/api` for Typescript SDK and `mobile/openapi` for Dart SDK.
|
||||||
|
|
||||||
|
## Database migrations
|
||||||
|
|
||||||
|
After making any changes in the `server/libs/database/src/entities`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration.
|
||||||
|
|
||||||
|
1. Attached to the server container shell.
|
||||||
|
2. Run
|
||||||
|
```bash
|
||||||
|
npm run typeorm -- migration:generate ./libs/database/src/<migration-name> -d libs/database/src/config/database.config.ts
|
||||||
|
```
|
||||||
|
3. Check if the migration file makes sense.
|
||||||
|
4. Move the migration file to folder `server/libs/database/src/migrations` in your code editor.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ The script will perform the following actions:
|
|||||||
|
|
||||||
The web application will be available at `http://<machine-ip-address>:2283`, and the server URL for the mobile app will be `http://<machine-ip-address>:2283/api`
|
The web application will be available at `http://<machine-ip-address>:2283`, and the server URL for the mobile app will be `http://<machine-ip-address>:2283/api`
|
||||||
|
|
||||||
The directory which is used to store the backup file is `./immich-app/immich-data` relative to the current directory.
|
The directory which is used to store the library files is `./immich-data` relative to the current directory.
|
||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
For more information on how to use the application, please refer to the [Post Installation](/docs/usage/post-installation) guide.
|
For more information on how to use the application, please refer to the [Post Installation](/docs/usage/post-installation) guide.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Install Immich using Portainer's Stack feature.
|
|||||||
1. Go to "**Stacks**" in the left sidebar.
|
1. Go to "**Stacks**" in the left sidebar.
|
||||||
2. Click on "**Add stack**".
|
2. Click on "**Add stack**".
|
||||||
3. Give the stack a name (i.e. Immich), and select "**Web Editor**" as the build method.
|
3. Give the stack a name (i.e. Immich), and select "**Web Editor**" as the build method.
|
||||||
4. Copy the content of the `docker-compose.yml` file from the [GitHub repository](https://raw.githubusercontent.com/immich-app/immich/main/docker/docker-compose.yml)
|
4. Copy the content of the `docker-compose.yml` file from the [GitHub repository](https://raw.githubusercontent.com/immich-app/immich/main/docker/docker-compose.yml).
|
||||||
5. Replace `.env` with `stack.env` for all containers that need to use environment variables in the web editor.
|
5. Replace `.env` with `stack.env` for all containers that need to use environment variables in the web editor.
|
||||||
|
|
||||||
<img
|
<img
|
||||||
@@ -28,7 +28,7 @@ Install Immich using Portainer's Stack feature.
|
|||||||
alt="Dot Env Example"
|
alt="Dot Env Example"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
9. Copy the content of the `.env.example` file from the [GitHub repository](https://raw.githubusercontent.com/immich-app/immich/main/docker/.env.example) and paste to the editor.
|
9. Copy the content of the `.env.example` file from the [GitHub repository](https://raw.githubusercontent.com/immich-app/immich/main/docker/.env.example) and paste into the editor.
|
||||||
10. Switch back to "**Simple Mode**".
|
10. Switch back to "**Simple Mode**".
|
||||||
|
|
||||||
<img
|
<img
|
||||||
@@ -39,8 +39,8 @@ Install Immich using Portainer's Stack feature.
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
* Populate custom database information if necessary.
|
* Populate custom database information if necessary.
|
||||||
* Populate `UPLOAD_LOCATION` as prefered location for storing backup assets.
|
* Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets.
|
||||||
* Populate a secret value for `JWT_SECRET`, you can use the command below to generate a secured key
|
* Populate a secret value for `JWT_SECRET`. You can use the command below to generate a secure key:
|
||||||
|
|
||||||
```bash title="Generate secure JWT_SECRET key"
|
```bash title="Generate secure JWT_SECRET key"
|
||||||
openssl rand -base64 128
|
openssl rand -base64 128
|
||||||
|
|||||||
@@ -69,9 +69,7 @@ LOG_LEVEL=simple
|
|||||||
# This JWT_SECRET is used to sign the authentication keys for user login
|
# This JWT_SECRET is used to sign the authentication keys for user login
|
||||||
# You should set it to a long randomly generated value
|
# You should set it to a long randomly generated value
|
||||||
# You can use this command to generate one: openssl rand -base64 128
|
# You can use this command to generate one: openssl rand -base64 128
|
||||||
JWT_SECRET=kWPdavjCECB0yoXgUHA/vpwpIKdCi/4ODVLIOe9WIi6AQlFfjWEuIVhWT3DtJE+T
|
JWT_SECRET=
|
||||||
CTckJnpwGgSK5AoqD+A8DZKsHCRdfVnlQIVqqmyR8isZTcxL5DWYQUSDRzyOO5OA
|
|
||||||
ZRUTE63FxiYhrRoe/y1yr5mV1osGy6mm6NZW8T2Tjwc=
|
|
||||||
|
|
||||||
###################################################################################
|
###################################################################################
|
||||||
# Reverse Geocoding
|
# Reverse Geocoding
|
||||||
@@ -102,8 +100,8 @@ PUBLIC_LOGIN_PAGE_MESSAGE="My Family Photos and Videos Backup Server"
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
* Populate custom database information if necessary.
|
* Populate custom database information if necessary.
|
||||||
* Populate `UPLOAD_LOCATION` as prefered location for storing backup assets.
|
* Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets.
|
||||||
* Populate a secret value for `JWT_SECRET`, you can use the command below to generate a secure key
|
* Populate a secret value for `JWT_SECRET`. You can use the command below to generate a secure key:
|
||||||
|
|
||||||
```bash title="Command to generate secure JWT_SECRET key"
|
```bash title="Command to generate secure JWT_SECRET key"
|
||||||
openssl rand -base64 128
|
openssl rand -base64 128
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ Hardware and software requirements for Immich
|
|||||||
- [Docker Compose](https://docs.docker.com/compose/install/)
|
- [Docker Compose](https://docs.docker.com/compose/install/)
|
||||||
|
|
||||||
:::info Podman
|
:::info Podman
|
||||||
You can also use Podman to run the application. However, additional configurations might be required on your end.
|
You can also use Podman to run the application. However, additional configuration might be required on your end.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Hardware
|
## Hardware
|
||||||
|
|
||||||
- **OS**: Preferred unix-based operating system (Ubuntu, Debian, MacOS...etc). Windows works too, with [Docker Desktop on Windows](https://docs.docker.com/desktop/install/windows-install/)
|
- **OS**: Preferred unix-based operating system (Ubuntu, Debian, MacOS, etc). Windows works too, with [Docker Desktop on Windows](https://docs.docker.com/desktop/install/windows-install/)
|
||||||
- **Ram**: At least 2GB, preferred 4GB.
|
- **RAM**: At least 2GB, preferred 4GB.
|
||||||
- **Core**: At least 2 cores, preferred 4 cores.
|
- **CPU**: At least 2 cores, preferred 4 cores.
|
||||||
|
|
||||||
## Installation methods
|
## Installation methods
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ sidebar_position: 5
|
|||||||
Install Immich on Unraid.
|
Install Immich on Unraid.
|
||||||
|
|
||||||
:::info Community contribution
|
:::info Community contribution
|
||||||
Please follow this community contributed [article](https://mfaz.dev/posts/immich-unraid/) to install Immich on Unraid.
|
Please follow [this community contributed article](https://mfaz.dev/posts/immich-unraid/) to install Immich on Unraid.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ I really like the Japanese culture, especially the books, history, and food. The
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
One of my favorite books is [Taikō](https://www.goodreads.com/book/show/336228.Taiko), it is the story about a prominent figure in the history of Japan, [Toyotomy Hideyoshi](https://www.britannica.com/biography/Toyotomi-Hideyoshi). He came from nothing, and through his resilience and wonderful mind, he has become one of the most powerful rulers in Japan's history. I enjoy his personality and the way he moved through life.
|
One of my favorite books is [Taikō](https://www.goodreads.com/book/show/336228.Taiko), it is a story about a prominent figure in the history of Japan, [Toyotomy Hideyoshi](https://www.britannica.com/biography/Toyotomi-Hideyoshi). He came from nothing, and through his resilience and wonderful mind, he has become one of the most powerful rulers in Japan's history. I enjoy his personality and the way he moved through life.
|
||||||
|
|
||||||
The color is an adaptation of **_App-Which-Must-Not-Be-Named_**'s color scheme, with an extra color (pink) to complete the flower's fifth petal. The petal layers are the same color scheme as the main layer rotating back and forth to "bring the flower to life."
|
The color is an adaptation of **_App-Which-Must-Not-Be-Named_**'s color scheme, with an extra color (pink) to complete the flower's fifth petal. The petal layers are the same color scheme as the main layer rotating back and forth to "bring the flower to life."
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ sidebar_position: 3
|
|||||||
|
|
||||||
I've committed to this project, and I will not stop. I will keep updating the docs, adding new features, and fixing bugs. But I can't do it alone, so I need your help to give me additional motivation to keep going.
|
I've committed to this project, and I will not stop. I will keep updating the docs, adding new features, and fixing bugs. But I can't do it alone, so I need your help to give me additional motivation to keep going.
|
||||||
|
|
||||||
As our hosts in the [selfhosted.show - In the episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) said, this is a massive undertaking; what the team and I are doing. I would love to someday be able to do this full-time, and I am asking for your help to make that happen.
|
As our hosts in the [selfhosted.show - In the episode 'The-organization-which-must-not-be-named is a Hostile Actor'](https://selfhosted.show/79?t=1418) said, this is a massive undertaking that the team and I are doing. I would love to someday be able to do this full-time, and I am asking for your help to make that happen.
|
||||||
|
|
||||||
If you feel like this is the right cause and the app is something you see yourself using for a long time, please consider supporting the project with the options below.
|
If you feel like this is the right cause and the app is something you see yourself using for a long time, please consider supporting the project with the options below.
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ sidebar_position: 4
|
|||||||
|
|
||||||
# Technology stack
|
# Technology stack
|
||||||
|
|
||||||
The app is built with the following technologies
|
The app is built with the following technologies:
|
||||||
|
|
||||||
## Frontend
|
## Frontend
|
||||||
* [Flutter](https://flutter.dev/) for the mobile app
|
* [Flutter](https://flutter.dev/) for the mobile app
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ A native Android notification shows up when the background upload is in progress
|
|||||||
:::note
|
:::note
|
||||||
* The app must be in the background for the backup worker to start running.
|
* The app must be in the background for the backup worker to start running.
|
||||||
* It is a well-known problem that some Android models are very strict with battery optimization settings, which can cause a problem with the background worker. Please visit [Don't kill my app](https://dontkillmyapp.com/) for a guide on disabling this setting on your phone.
|
* It is a well-known problem that some Android models are very strict with battery optimization settings, which can cause a problem with the background worker. Please visit [Don't kill my app](https://dontkillmyapp.com/) for a guide on disabling this setting on your phone.
|
||||||
* If you reopen the app and the first page you see is the backup page, the counts will reflect the background uploaded result. You have to navigate out of the page and come back to see the updated counts.
|
* If you reopen the app and the first page you see is the backup page, the counts will not reflect the background uploaded result. You have to navigate out of the page and come back to see the updated counts.
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -8,17 +8,19 @@ You can use the CLI to upload an existing gallery to the Immich server
|
|||||||
|
|
||||||
[Immich CLI Repository](https://github.com/immich-app/CLI)
|
[Immich CLI Repository](https://github.com/immich-app/CLI)
|
||||||
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
* Node.js 16 or above
|
|
||||||
* Npm
|
- Node.js 16 or above
|
||||||
|
- Npm
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm i -g immich
|
npm i -g immich
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
Specify user's credentials, Immich's server address and port, and the directory you would like to upload videos/photos from.
|
Specify user's credentials, Immich's server address and port, and the directory you would like to upload videos/photos from.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -58,10 +60,9 @@ immich upload --email testuser@email.com --password password --server http://192
|
|||||||
### Run from source
|
### Run from source
|
||||||
|
|
||||||
```bash title="Clone Repository"
|
```bash title="Clone Repository"
|
||||||
git clone https://github.com/alextran1502/immich-cli
|
git clone https://github.com/immich-app/CLI
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```bash title="Install dependencies"
|
```bash title="Install dependencies"
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|||||||
BIN
docs/docs/usage/img/authentik-redirect.png
Normal file
BIN
docs/docs/usage/img/authentik-redirect.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
76
docs/docs/usage/oauth.md
Normal file
76
docs/docs/usage/oauth.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
# OAuth Authentication
|
||||||
|
|
||||||
|
This page contains details about using OAuth 2 in Immich.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an identity layer built on top of OAuth2. OIDC is supported by most identity providers, including:
|
||||||
|
|
||||||
|
- [Authentik](https://goauthentik.io/integrations/sources/oauth/#openid-connect)
|
||||||
|
- [Authelia](https://www.authelia.com/configuration/identity-providers/open-id-connect/)
|
||||||
|
- [Okta](https://www.okta.com/openid-connect/)
|
||||||
|
- [Google](https://developers.google.com/identity/openid-connect/openid-connect)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before enabling OAuth in Immich, a new client application needs to be configured in the 3rd-party authentication server. While the specifics of this setup vary from provider to provider, the general approach should be the same.
|
||||||
|
|
||||||
|
1. Create a new (Client) Application
|
||||||
|
|
||||||
|
1. The **Provider** type should be `OpenID Connect` or `OAuth2`
|
||||||
|
2. The **Client type** should be `Confidential`
|
||||||
|
3. The **Application** type should be `Web`
|
||||||
|
4. The **Grant** type should be `Authorization Code`
|
||||||
|
|
||||||
|
2. Configure Redirect URIs/Origins
|
||||||
|
|
||||||
|
The **Sign-in redirect URIs** should include:
|
||||||
|
|
||||||
|
* All URLs that will be used to access the login page of the Immich web client (eg. `http://localhost:2283/auth/login`, `http://192.168.0.200:2283/auth/login`, `https://immich.example.com/auth/login`)
|
||||||
|
* Mobile app redirect URL `app.immich:/`
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
You **MUST** include `app.immich:/` as the redirect URI for iOS and Android mobile app to work properly.
|
||||||
|
|
||||||
|
**Authentik example**
|
||||||
|
<img src={require('./img/authentik-redirect.png').default} title="Authentik Redirection URL" width="80%" />
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Enable OAuth
|
||||||
|
|
||||||
|
Once you have a new OAuth client application configured, Immich can be configured using the following environment variables:
|
||||||
|
|
||||||
|
| Key | Type | Default | Description |
|
||||||
|
| ------------------- | ------- | -------------------- | ------------------------------------------------------------------------- |
|
||||||
|
| OAUTH_ENABLED | boolean | false | Enable/disable OAuth2 |
|
||||||
|
| OAUTH_ISSUER_URL | URL | (required) | Required. Self-discovery URL for client (from previous step) |
|
||||||
|
| OAUTH_CLIENT_ID | string | (required) | Required. Client ID (from previous step) |
|
||||||
|
| OAUTH_CLIENT_SECRET | string | (required) | Required. Client Secret (previous step |
|
||||||
|
| OAUTH_SCOPE | string | openid email profile | Full list of scopes to send with the request (space delimited) |
|
||||||
|
| OAUTH_AUTO_REGISTER | boolean | true | When true, will automatically register a user the first time they sign in |
|
||||||
|
| OAUTH_BUTTON_TEXT | string | Login with OAuth | Text for the OAuth button on the web |
|
||||||
|
|
||||||
|
:::info
|
||||||
|
The Issuer URL should look something like the following, and return a valid json document.
|
||||||
|
|
||||||
|
- `https://accounts.google.com/.well-known/openid-configuration`
|
||||||
|
- `http://localhost:9000/application/o/immich/.well-known/openid-configuration`
|
||||||
|
|
||||||
|
The `.well-known/openid-configuration` part of the url is optional and will be automatically added during discovery.
|
||||||
|
:::
|
||||||
|
|
||||||
|
Here is an example of a valid configuration for setting up Immich to use OAuth with Authentik:
|
||||||
|
|
||||||
|
```
|
||||||
|
OAUTH_ENABLED=true
|
||||||
|
OAUTH_ISSUER_URL=http://192.168.0.187:9000/application/o/immich
|
||||||
|
OAUTH_CLIENT_ID=f08f9c5b4f77dcfd3916b1c032336b5544a7b368
|
||||||
|
OAUTH_CLIENT_SECRET=6fe2e697644da6ff6aef73387a457d819018189086fa54b151a6067fbb884e75f7e5c90be16d3c688cf902c6974817a85eab93007d76675041eaead8c39cf5a2
|
||||||
|
OAUTH_BUTTON_TEXT=Login with Authentik
|
||||||
|
```
|
||||||
|
|
||||||
|
[oidc]: https://openid.net/connect/
|
||||||
@@ -58,7 +58,7 @@ const config = {
|
|||||||
({
|
({
|
||||||
announcementBar: {
|
announcementBar: {
|
||||||
id: "site_announcement_immich",
|
id: "site_announcement_immich",
|
||||||
content: `⚠️ The project is under <strong>very active</strong> development. Expect bugs and changes. Do not use as a <strong>single source</strong> to store of your photos and videos!`,
|
content: `⚠️ The project is under <strong>very active</strong> development. Expect bugs and changes. Do not use it as <strong>the only way</strong> to store your photos and videos!`,
|
||||||
backgroundColor: "#593f00",
|
backgroundColor: "#593f00",
|
||||||
textColor: "#ffefc9",
|
textColor: "#ffefc9",
|
||||||
isCloseable: false,
|
isCloseable: false,
|
||||||
|
|||||||
@@ -54,3 +54,13 @@
|
|||||||
.introButton:hover {
|
.introButton:hover {
|
||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.demoButton {
|
||||||
|
background-color: aquamarine;
|
||||||
|
color: #000000;
|
||||||
|
border-radius: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demoButton:hover {
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
@@ -40,6 +40,15 @@ function HomepageHeader() {
|
|||||||
Installation
|
Installation
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.buttons}>
|
||||||
|
<Link
|
||||||
|
className={clsx("button button--lg", styles.demoButton)}
|
||||||
|
to="https://demo.immich.app/"
|
||||||
|
>
|
||||||
|
Demo
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src="/img/immich-screenshots.webp" alt="logo" />
|
<img src="/img/immich-screenshots.webp" alt="logo" />
|
||||||
|
|||||||
@@ -1,43 +1,42 @@
|
|||||||
|
|
||||||
# Build stage
|
|
||||||
FROM node:16-bullseye-slim as builder
|
FROM node:16-bullseye-slim as builder
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
|
||||||
|
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
RUN apt-get install gcc g++ make cmake python3 python3-pip ffmpeg -y
|
RUN apt-get install gcc g++ make cmake python3 python3-pip ffmpeg -y
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
RUN npm rebuild @tensorflow/tfjs-node --build-from-source
|
RUN npm rebuild @tensorflow/tfjs-node --build-from-source
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
|
||||||
|
FROM builder as prod
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
|
RUN npm prune --omit=dev
|
||||||
|
|
||||||
|
|
||||||
# Prod stage
|
|
||||||
FROM node:16-bullseye-slim
|
FROM node:16-bullseye-slim
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
RUN apt-get update \
|
||||||
COPY entrypoint.sh ./
|
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/app/dist \
|
|
||||||
&& mkdir -p /usr/src/app/node_modules \
|
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y ffmpeg \
|
&& apt-get install -y ffmpeg \
|
||||||
&& rm -rf /var/cache/apt/lists
|
&& rm -rf /var/cache/apt/lists
|
||||||
|
|
||||||
COPY --from=builder /usr/src/app/node_modules ./node_modules
|
COPY --from=prod /usr/src/app/node_modules ./node_modules
|
||||||
COPY --from=builder /usr/src/app/dist ./dist
|
COPY --from=prod /usr/src/app/dist ./dist
|
||||||
|
|
||||||
RUN npm prune --production
|
COPY package.json package-lock.json ./
|
||||||
|
COPY entrypoint.sh ./
|
||||||
|
|
||||||
# CMD [ "node", "dist/main" ]
|
# CMD [ "node", "dist/main" ]
|
||||||
@@ -33,7 +33,7 @@ if (keystorePropertiesFile.exists()) {
|
|||||||
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion flutter.compileSdkVersion
|
compileSdkVersion 33
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
|||||||
@@ -12,15 +12,26 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="com.linusu.flutter_web_auth.CallbackActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter android:label="flutter_web_auth">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="app.immich" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
<meta-data android:name="flutterEmbedding" android:value="2" />
|
<meta-data android:name="flutterEmbedding" android:value="2" />
|
||||||
<!-- Disables default WorkManager initialization to use our custom initialization -->
|
<!-- Disables default WorkManager initialization to use our custom initialization -->
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.startup.InitializationProvider"
|
android:name="androidx.startup.InitializationProvider"
|
||||||
android:authorities="${applicationId}.androidx-startup"
|
android:authorities="${applicationId}.androidx-startup"
|
||||||
tools:node="remove">
|
tools:node="remove"></provider>
|
||||||
</provider>
|
|
||||||
</application>
|
</application>
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 54,
|
"android.injected.version.code" => 55,
|
||||||
"android.injected.version.name" => "1.35.0",
|
"android.injected.version.name" => "1.36.0",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
* Added OAuth login option
|
||||||
|
* Tidy-up dependencies, remove unused, replace rarely used ones
|
||||||
|
* Added view LivePhotos feature
|
||||||
@@ -15,7 +15,7 @@ Once set up, this app can be used as photo and video backup solution directly fr
|
|||||||
* 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 <a href='https://www.npmjs.com/package/immich' target='_blank' rel='nofollow'>immich cli tools</a>
|
* 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)
|
* Reverse geocoding from image exif data
|
||||||
* 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
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000233">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000315">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="61.699536">
|
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="185.624188">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="46.210553">
|
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="39.180655">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
This is a client app for Immich Server and you will need to run/manage the server on your own in order to use the app.
|
This is a client app for Immich Server and you will need to run/manage the server on your own in order to use the app.
|
||||||
|
|
||||||
Github URL: https://github.com/alextran1502/immich
|
Github URL: https://github.com/immich-app/immich
|
||||||
|
|||||||
@@ -109,7 +109,9 @@
|
|||||||
"login_form_err_invalid_email": "Invalid Email",
|
"login_form_err_invalid_email": "Invalid Email",
|
||||||
"login_form_err_leading_whitespace": "Leading whitespace",
|
"login_form_err_leading_whitespace": "Leading whitespace",
|
||||||
"login_form_err_trailing_whitespace": "Trailing whitespace",
|
"login_form_err_trailing_whitespace": "Trailing whitespace",
|
||||||
"login_form_failed_login": "Error logging you in, check server url, email and password",
|
"login_form_failed_login": "Error logging you in, check server URL, email and password",
|
||||||
|
"login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL",
|
||||||
|
"login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server",
|
||||||
"login_form_label_email": "Email",
|
"login_form_label_email": "Email",
|
||||||
"login_form_label_password": "Password",
|
"login_form_label_password": "Password",
|
||||||
"login_form_password_hint": "password",
|
"login_form_password_hint": "password",
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"album_info_card_backup_album_excluded": "제외됨",
|
"album_info_card_backup_album_excluded": "제외됨",
|
||||||
"album_info_card_backup_album_included": "포함됨",
|
"album_info_card_backup_album_included": "포함됨",
|
||||||
|
"album_thumbnail_card_item": "1개 항목",
|
||||||
|
"album_thumbnail_card_items": "{}개 항목",
|
||||||
|
"album_thumbnail_card_shared": " · 공유",
|
||||||
"album_viewer_appbar_share_delete": "앨범 삭제",
|
"album_viewer_appbar_share_delete": "앨범 삭제",
|
||||||
"album_viewer_appbar_share_err_delete": "앨범 삭제 실패",
|
"album_viewer_appbar_share_err_delete": "앨범 삭제 실패",
|
||||||
"album_viewer_appbar_share_err_leave": "앨범에서 나가지 못했습니다",
|
"album_viewer_appbar_share_err_leave": "앨범에서 나가지 못했습니다",
|
||||||
@@ -9,6 +12,8 @@
|
|||||||
"album_viewer_appbar_share_leave": "앨범 나가기",
|
"album_viewer_appbar_share_leave": "앨범 나가기",
|
||||||
"album_viewer_appbar_share_remove": "앨범에서 제거",
|
"album_viewer_appbar_share_remove": "앨범에서 제거",
|
||||||
"album_viewer_page_share_add_users": "사용자 추가",
|
"album_viewer_page_share_add_users": "사용자 추가",
|
||||||
|
"asset_list_settings_subtitle": "사진 배열 레이아웃 설정",
|
||||||
|
"asset_list_settings_title": "사진 배열",
|
||||||
"backup_album_selection_page_albums_device": "기기의 앨범({})",
|
"backup_album_selection_page_albums_device": "기기의 앨범({})",
|
||||||
"backup_album_selection_page_albums_tap": "포함하려면 탭하고 제외하려면 두 번 탭하세요",
|
"backup_album_selection_page_albums_tap": "포함하려면 탭하고 제외하려면 두 번 탭하세요",
|
||||||
"backup_album_selection_page_assets_scatter": "미디어파일은 여러 앨범에 분산될 수 있습니다. 따라서 백업 프로세스 중에 앨범에서 포함하거나 제외할 수 있습니다.",
|
"backup_album_selection_page_assets_scatter": "미디어파일은 여러 앨범에 분산될 수 있습니다. 따라서 백업 프로세스 중에 앨범에서 포함하거나 제외할 수 있습니다.",
|
||||||
@@ -16,25 +21,29 @@
|
|||||||
"backup_album_selection_page_selection_info": "선택 정보",
|
"backup_album_selection_page_selection_info": "선택 정보",
|
||||||
"backup_album_selection_page_total_assets": "총 미디어파일 수",
|
"backup_album_selection_page_total_assets": "총 미디어파일 수",
|
||||||
"backup_all": "모두",
|
"backup_all": "모두",
|
||||||
"backup_background_service_default_notification": "새 미디어파일 확인중...",
|
|
||||||
"backup_background_service_upload_failure_notification": "{} 업로드 실패",
|
|
||||||
"backup_background_service_in_progress_notification": "미디어파일 백업 중...",
|
|
||||||
"backup_background_service_current_upload_notification": "{} 업로드 중",
|
|
||||||
"backup_background_service_error_title": "백업 오류",
|
|
||||||
"backup_background_service_connection_failed_message": "서버에 연결하지 못했습니다. 다시 시도하는 중...",
|
|
||||||
"backup_background_service_backup_failed_message": "미디어파일을 백업하지 못했습니다. 다시 시도하는 중...",
|
"backup_background_service_backup_failed_message": "미디어파일을 백업하지 못했습니다. 다시 시도하는 중...",
|
||||||
|
"backup_background_service_connection_failed_message": "서버에 연결하지 못했습니다. 다시 시도하는 중...",
|
||||||
|
"backup_background_service_current_upload_notification": "{} 업로드 중",
|
||||||
|
"backup_background_service_default_notification": "새 미디어파일 확인중...",
|
||||||
|
"backup_background_service_error_title": "백업 오류",
|
||||||
|
"backup_background_service_in_progress_notification": "미디어파일 백업 중...",
|
||||||
|
"backup_background_service_upload_failure_notification": "{} 업로드 실패",
|
||||||
"backup_controller_page_albums": "백업대상",
|
"backup_controller_page_albums": "백업대상",
|
||||||
|
"backup_controller_page_background_battery_info_link": "사용 가이드",
|
||||||
|
"backup_controller_page_background_battery_info_message": "최상의 백업 환경을 위해 Immich 앱의 백그라운드 활동을 제한하는 배터리 최적화기능을 꺼주세요.\n\n휴대폰마다 설정방법이 다르므로 제조업체별로 설정방법을 확인하세요.",
|
||||||
|
"backup_controller_page_background_battery_info_ok": "확인",
|
||||||
|
"backup_controller_page_background_battery_info_title": "배터리 최적화",
|
||||||
|
"backup_controller_page_background_charging": "충전 중일 때만",
|
||||||
|
"backup_controller_page_background_configure_error": "백그라운드 서비스를 구성하지 못했습니다",
|
||||||
|
"backup_controller_page_background_description": "백그라운드 서비스를 켜서 앱을 열지 않고도 새 미디어파일을 자동으로 백업합니다.",
|
||||||
|
"backup_controller_page_background_is_off": "자동 백그라운드 백업이 꺼져 있습니다",
|
||||||
|
"backup_controller_page_background_is_on": "자동 백그라운드 백업이 켜져 있습니다",
|
||||||
|
"backup_controller_page_background_turn_off": "백그라운드 서비스 끄기",
|
||||||
|
"backup_controller_page_background_turn_on": "백그라운드 서비스 켜기",
|
||||||
|
"backup_controller_page_background_wifi": "WiFi에서만",
|
||||||
"backup_controller_page_backup": "백업",
|
"backup_controller_page_backup": "백업",
|
||||||
"backup_controller_page_backup_selected": "선택됨: ",
|
"backup_controller_page_backup_selected": "선택됨: ",
|
||||||
"backup_controller_page_backup_sub": "백업된 사진 및 비디오",
|
"backup_controller_page_backup_sub": "백업된 사진 및 비디오",
|
||||||
"backup_controller_page_background_description": "백그라운드 서비스를 켜서 앱을 열지 않고도 새 미디어파일을 자동으로 백업합니다.",
|
|
||||||
"backup_controller_page_background_wifi": "WiFi에서만",
|
|
||||||
"backup_controller_page_background_charging": "충전 중일 때만",
|
|
||||||
"backup_controller_page_background_is_on": "자동 백그라운드 백업이 켜져 있습니다",
|
|
||||||
"backup_controller_page_background_is_off": "자동 백그라운드 백업이 꺼져 있습니다",
|
|
||||||
"backup_controller_page_background_turn_on": "백그라운드 서비스 켜기",
|
|
||||||
"backup_controller_page_background_turn_off": "백그라운드 서비스 끄기",
|
|
||||||
"backup_controller_page_background_configure_error": "백그라운드 서비스를 구성하지 못했습니다",
|
|
||||||
"backup_controller_page_cancel": "취소",
|
"backup_controller_page_cancel": "취소",
|
||||||
"backup_controller_page_created": "생성일: {}",
|
"backup_controller_page_created": "생성일: {}",
|
||||||
"backup_controller_page_desc_backup": "새 미디어파일을 서버에 자동으로 업로드하려면 백업을 켜주세요.",
|
"backup_controller_page_desc_backup": "새 미디어파일을 서버에 자동으로 업로드하려면 백업을 켜주세요.",
|
||||||
@@ -60,9 +69,28 @@
|
|||||||
"backup_controller_page_uploading_file_info": "파일 정보 업로드 중",
|
"backup_controller_page_uploading_file_info": "파일 정보 업로드 중",
|
||||||
"backup_err_only_album": "유일한 앨범은 제거할 수 없습니다",
|
"backup_err_only_album": "유일한 앨범은 제거할 수 없습니다",
|
||||||
"backup_info_card_assets": "미디어",
|
"backup_info_card_assets": "미디어",
|
||||||
|
"cache_settings_album_thumbnails": "라이브러리 페이지 썸네일 ({} 미디어)",
|
||||||
|
"cache_settings_clear_cache_button": "캐시 지우기",
|
||||||
|
"cache_settings_clear_cache_button_title": "앱의 캐시를 지웁니다. 이 작업은 캐시가 다시 빌드될 때까지 앱의 성능에 상당한 영향을 미칩니다.",
|
||||||
|
"cache_settings_image_cache_size": "이미재 캐시 크기 ({} 미디어)",
|
||||||
|
"cache_settings_statistics_album": "라이브러리 썸네일",
|
||||||
|
"cache_settings_statistics_assets": "{} 미디어 ({})",
|
||||||
|
"cache_settings_statistics_full": "전체 이미지",
|
||||||
|
"cache_settings_statistics_shared": "공유 앨범 썸네일",
|
||||||
|
"cache_settings_statistics_thumbnail": "썸네일",
|
||||||
|
"cache_settings_statistics_title": "캐시 사용률",
|
||||||
|
"cache_settings_subtitle": "Immich 앱의 캐싱 동작 제어",
|
||||||
|
"cache_settings_thumbnail_size": "썸네일 캐시 크기 ({} 미디어)",
|
||||||
|
"cache_settings_title": "캐시 설정",
|
||||||
|
"control_bottom_app_bar_add_to_album": "앨범에 추가",
|
||||||
|
"control_bottom_app_bar_album_info": "{} 항목",
|
||||||
|
"control_bottom_app_bar_album_info_shared": "{} 항목 · 공유됨",
|
||||||
|
"control_bottom_app_bar_create_new_album": "앨범 생성",
|
||||||
"control_bottom_app_bar_delete": "삭제",
|
"control_bottom_app_bar_delete": "삭제",
|
||||||
"create_shared_album_page_share": "공유",
|
"control_bottom_app_bar_share": "공유",
|
||||||
|
"create_album_page_untitled": "제목없음",
|
||||||
"create_shared_album_page_create": "만들기",
|
"create_shared_album_page_create": "만들기",
|
||||||
|
"create_shared_album_page_share": "공유",
|
||||||
"create_shared_album_page_share_add_assets": "사진 추가",
|
"create_shared_album_page_share_add_assets": "사진 추가",
|
||||||
"create_shared_album_page_share_select_photos": "사진 선택",
|
"create_shared_album_page_share_select_photos": "사진 선택",
|
||||||
"daily_title_text_date": "E, M월 d일",
|
"daily_title_text_date": "E, M월 d일",
|
||||||
@@ -75,6 +103,12 @@
|
|||||||
"exif_bottom_sheet_description": "설명 추가...",
|
"exif_bottom_sheet_description": "설명 추가...",
|
||||||
"exif_bottom_sheet_details": "상세정보",
|
"exif_bottom_sheet_details": "상세정보",
|
||||||
"exif_bottom_sheet_location": "위치",
|
"exif_bottom_sheet_location": "위치",
|
||||||
|
"experimental_settings_subtitle": "문제시 책임지지 않습니다!",
|
||||||
|
"experimental_settings_title": "실험적기능",
|
||||||
|
"home_page_add_to_album_conflicts": "{album} 앨범에 {added} 미디어를 추가했습니다. {failed} 이미 앨범에 있는 항목입니다.",
|
||||||
|
"home_page_add_to_album_success": "{album} 앨범에 {added} 미디어를 추가했습니다. ",
|
||||||
|
"library_page_albums": "앨범",
|
||||||
|
"library_page_new_album": "새 앨범",
|
||||||
"login_form_button_text": "로그인",
|
"login_form_button_text": "로그인",
|
||||||
"login_form_email_hint": "youremail@email.com",
|
"login_form_email_hint": "youremail@email.com",
|
||||||
"login_form_endpoint_hint": "https://your-server-ip:port/api",
|
"login_form_endpoint_hint": "https://your-server-ip:port/api",
|
||||||
@@ -90,8 +124,8 @@
|
|||||||
"login_form_save_login": "로그인상태 유지",
|
"login_form_save_login": "로그인상태 유지",
|
||||||
"monthly_title_text_date_format": "y년 M월",
|
"monthly_title_text_date_format": "y년 M월",
|
||||||
"profile_drawer_client_server_up_to_date": "클라이언트와 서버가 최신 상태입니다",
|
"profile_drawer_client_server_up_to_date": "클라이언트와 서버가 최신 상태입니다",
|
||||||
"profile_drawer_sign_out": "로그아웃",
|
|
||||||
"profile_drawer_settings": "설정",
|
"profile_drawer_settings": "설정",
|
||||||
|
"profile_drawer_sign_out": "로그아웃",
|
||||||
"search_bar_hint": "사진 검색",
|
"search_bar_hint": "사진 검색",
|
||||||
"search_page_no_objects": "발견된 사물이\n없습니다",
|
"search_page_no_objects": "발견된 사물이\n없습니다",
|
||||||
"search_page_no_places": "발견된 장소가\n없습니다",
|
"search_page_no_places": "발견된 장소가\n없습니다",
|
||||||
@@ -101,52 +135,47 @@
|
|||||||
"select_additional_user_for_sharing_page_suggestions": "초대 가능한 사용자 제안",
|
"select_additional_user_for_sharing_page_suggestions": "초대 가능한 사용자 제안",
|
||||||
"select_user_for_sharing_page_err_album": "앨범 생성 실패",
|
"select_user_for_sharing_page_err_album": "앨범 생성 실패",
|
||||||
"select_user_for_sharing_page_share_suggestions": "초대 가능한 사용자 제안",
|
"select_user_for_sharing_page_share_suggestions": "초대 가능한 사용자 제안",
|
||||||
|
"setting_notifications_notify_failures_grace_period": "백그라운드 백업 실패 알림: {}",
|
||||||
|
"setting_notifications_notify_hours": "{}시간 뒤",
|
||||||
|
"setting_notifications_notify_immediately": "즉시",
|
||||||
|
"setting_notifications_notify_minutes": "{}분 뒤",
|
||||||
|
"setting_notifications_notify_never": "알리지 않음",
|
||||||
|
"setting_notifications_single_progress_subtitle": "미디어별 상세 진행률 표시",
|
||||||
|
"setting_notifications_single_progress_title": "백그라운드 작업 세부 진행률 표시",
|
||||||
|
"setting_notifications_subtitle": "알림 기본 설정 조정",
|
||||||
|
"setting_notifications_title": "알림",
|
||||||
|
"setting_notifications_total_progress_subtitle": "전체 업로드 진행률(완료/전체)",
|
||||||
|
"setting_notifications_total_progress_title": "백그라운드 작업 전체 진행률 표시",
|
||||||
|
"setting_pages_app_bar_settings": "설정",
|
||||||
"share_add": "추가",
|
"share_add": "추가",
|
||||||
"share_add_photos": "사진 추가",
|
"share_add_photos": "사진 추가",
|
||||||
"share_add_title": "새 앨범제목",
|
"share_add_title": "새 앨범제목",
|
||||||
"share_create_album": "앨범 만들기",
|
"share_create_album": "앨범 만들기",
|
||||||
|
"share_dialog_preparing": "준비중...",
|
||||||
"share_invite": "앨범에 초대",
|
"share_invite": "앨범에 초대",
|
||||||
"sharing_page_album": "공유앨범",
|
"sharing_page_album": "공유앨범",
|
||||||
"sharing_page_description": "공유앨범을 만들어 다른 사용자들과 사진 및 비디오를 공유합니다.",
|
"sharing_page_description": "공유앨범을 만들어 다른 사용자들과 사진 및 비디오를 공유합니다.",
|
||||||
"sharing_page_empty_list": "공유앨범 없음",
|
"sharing_page_empty_list": "공유앨범 없음",
|
||||||
"sharing_silver_appbar_create_shared_album": "공유앨범 만들기",
|
"sharing_silver_appbar_create_shared_album": "공유앨범 만들기",
|
||||||
"sharing_silver_appbar_share_partner": "파트너와 공유",
|
"sharing_silver_appbar_share_partner": "파트너와 공유",
|
||||||
|
"tab_controller_nav_library": "라이브러리",
|
||||||
"tab_controller_nav_photos": "사진",
|
"tab_controller_nav_photos": "사진",
|
||||||
"tab_controller_nav_search": "검색",
|
"tab_controller_nav_search": "검색",
|
||||||
"tab_controller_nav_sharing": "공유",
|
"tab_controller_nav_sharing": "공유",
|
||||||
"tab_controller_nav_library": "라이브러리",
|
"theme_setting_asset_list_storage_indicator_title": "미디어 타일에 스토리지 싱크여부 표시",
|
||||||
|
"theme_setting_asset_list_tiles_per_row_title": "한 줄에 표시할 미디어 수 ({})",
|
||||||
|
"theme_setting_dark_mode_switch": "다크모드",
|
||||||
|
"theme_setting_image_viewer_quality_subtitle": "디테일 이미지 뷰어 품질 조정",
|
||||||
|
"theme_setting_image_viewer_quality_title": "이미지 뷰어 품질",
|
||||||
|
"theme_setting_system_theme_switch": "자동(시스템 설정에 따름)",
|
||||||
|
"theme_setting_theme_subtitle": "앱테마 선택",
|
||||||
|
"theme_setting_theme_title": "테마",
|
||||||
|
"theme_setting_three_stage_loading_subtitle": "이 기능은 로딩 성능을 향상시킬 수 있지만 훨씬 더 많은 데이터를 사용합니다.",
|
||||||
|
"theme_setting_three_stage_loading_title": "3단계 로딩 활성화",
|
||||||
"version_announcement_overlay_ack": "승인",
|
"version_announcement_overlay_ack": "승인",
|
||||||
"version_announcement_overlay_release_notes": "릴리스 정보",
|
"version_announcement_overlay_release_notes": "릴리스 정보",
|
||||||
"version_announcement_overlay_text_1": "안녕하세요!",
|
"version_announcement_overlay_text_1": "안녕하세요!",
|
||||||
"version_announcement_overlay_text_2": "앱에 새로운 업데이트가 있습니다!",
|
"version_announcement_overlay_text_2": "앱에 새로운 업데이트가 있습니다!",
|
||||||
"version_announcement_overlay_text_3": "특히 WatchTower 또는 서버 응용 프로그램 자동 업데이트를 처리하는 메커니즘을 사용하는 경우 잘못된 구성을 방지하기 위해 docker-compose 및 .env 설정이 최신 상태인지 확인하세요.",
|
"version_announcement_overlay_text_3": "특히 WatchTower 또는 서버 응용 프로그램 자동 업데이트를 처리하는 메커니즘을 사용하는 경우 잘못된 구성을 방지하기 위해 docker-compose 및 .env 설정이 최신 상태인지 확인하세요.",
|
||||||
"version_announcement_overlay_title": "새 서버 버전 사용 가능 \uD83C\uDF89",
|
"version_announcement_overlay_title": "새 서버 버전 사용 가능 🎉"
|
||||||
"album_thumbnail_card_item": "1개 항목",
|
|
||||||
"album_thumbnail_card_items": "{}개 항목",
|
|
||||||
"album_thumbnail_card_shared": " · 공유",
|
|
||||||
"library_page_albums": "앨범",
|
|
||||||
"library_page_new_album": "새 앨범",
|
|
||||||
"create_album_page_untitled": "제목없음",
|
|
||||||
"share_dialog_preparing": "준비중...",
|
|
||||||
"control_bottom_app_bar_share": "공유",
|
|
||||||
"setting_pages_app_bar_settings": "설정",
|
|
||||||
"theme_setting_theme_title": "테마",
|
|
||||||
"theme_setting_theme_subtitle": "앱테마 선택",
|
|
||||||
"theme_setting_system_theme_switch": "자동(시스템 설정에 따름)",
|
|
||||||
"theme_setting_dark_mode_switch": "다크모드",
|
|
||||||
"theme_setting_image_viewer_quality_title": "이미지 뷰어 품질",
|
|
||||||
"theme_setting_image_viewer_quality_subtitle": "디테일 이미지 뷰어 품질 조정",
|
|
||||||
"theme_setting_three_stage_loading_title": "3단계 로딩 활성화",
|
|
||||||
"theme_setting_three_stage_loading_subtitle": "이 기능은 로딩 성능을 향상시킬 수 있지만 훨씬 더 많은 데이터를 사용합니다.",
|
|
||||||
"asset_list_settings_title": "사진 배열",
|
|
||||||
"asset_list_settings_subtitle": "사진 배열 레이아웃 설정",
|
|
||||||
"theme_setting_asset_list_storage_indicator_title": "미디어 타일에 스토리지 싱크여부 표시",
|
|
||||||
"theme_setting_asset_list_tiles_per_row_title": "한 줄에 표시할 미디어 수 ({})",
|
|
||||||
"setting_notifications_title": "알림",
|
|
||||||
"setting_notifications_subtitle": "알림 기본 설정 조정",
|
|
||||||
"setting_notifications_notify_failures_grace_period": "백그라운드 백업 실패 알림: {}",
|
|
||||||
"setting_notifications_notify_immediately": "즉시",
|
|
||||||
"setting_notifications_notify_minutes": "{}분 뒤",
|
|
||||||
"setting_notifications_notify_hours": "{}시간 뒤",
|
|
||||||
"setting_notifications_notify_never": "알리지 않음"
|
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,8 @@ PODS:
|
|||||||
- flutter_udid (0.0.1):
|
- flutter_udid (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- SAMKeychain
|
- SAMKeychain
|
||||||
|
- flutter_web_auth (0.5.0):
|
||||||
|
- Flutter
|
||||||
- fluttertoast (0.0.2):
|
- fluttertoast (0.0.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Toast
|
- Toast
|
||||||
@@ -37,6 +39,7 @@ PODS:
|
|||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
|
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
|
||||||
|
- flutter_web_auth (from `.symlinks/plugins/flutter_web_auth/ios`)
|
||||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
@@ -60,6 +63,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter
|
:path: Flutter
|
||||||
flutter_udid:
|
flutter_udid:
|
||||||
:path: ".symlinks/plugins/flutter_udid/ios"
|
:path: ".symlinks/plugins/flutter_udid/ios"
|
||||||
|
flutter_web_auth:
|
||||||
|
:path: ".symlinks/plugins/flutter_web_auth/ios"
|
||||||
fluttertoast:
|
fluttertoast:
|
||||||
:path: ".symlinks/plugins/fluttertoast/ios"
|
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||||
image_picker_ios:
|
image_picker_ios:
|
||||||
@@ -86,6 +91,7 @@ EXTERNAL SOURCES:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
flutter_udid: 0848809dbed4c055175747ae6a45a8b4f6771e1c
|
flutter_udid: 0848809dbed4c055175747ae6a45a8b4f6771e1c
|
||||||
|
flutter_web_auth: c25208760459cec375a3c39f6a8759165ca0fa4d
|
||||||
fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037
|
fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037
|
||||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
|
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
|
||||||
|
|||||||
@@ -360,7 +360,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 68;
|
CURRENT_PROJECT_VERSION = 71;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -495,7 +495,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 68;
|
CURRENT_PROJECT_VERSION = 71;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -522,7 +522,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 68;
|
CURRENT_PROJECT_VERSION = 71;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
|||||||
@@ -17,11 +17,11 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.34.0</string>
|
<string>1.36.0</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>68</string>
|
<string>71</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true />
|
<true />
|
||||||
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
|
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
|
||||||
|
|||||||
@@ -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.35.0"
|
version_number: "1.36.0"
|
||||||
)
|
)
|
||||||
increment_build_number(
|
increment_build_number(
|
||||||
build_number: latest_testflight_build_number + 1,
|
build_number: latest_testflight_build_number + 1,
|
||||||
|
|||||||
@@ -5,32 +5,32 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000304">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000333">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="0.606546">
|
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="0.777934">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="3.706234">
|
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="6.375897">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="0.657686">
|
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="0.664307">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="4: build_app" time="68.78265">
|
<testcase classname="fastlane.lanes" name="4: build_app" time="88.90147">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="60.883182">
|
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="79.807067">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|||||||
@@ -22,28 +22,58 @@ class ImageViewerService {
|
|||||||
try {
|
try {
|
||||||
String fileName = p.basename(asset.originalPath);
|
String fileName = p.basename(asset.originalPath);
|
||||||
|
|
||||||
var res = await _apiService.assetApi.downloadFileWithHttpInfo(
|
// Download LivePhotos image and motion part
|
||||||
asset.deviceAssetId,
|
if (asset.type == AssetTypeEnum.IMAGE && asset.livePhotoVideoId != null) {
|
||||||
asset.deviceId,
|
var imageResponse = await _apiService.assetApi.downloadFileWithHttpInfo(
|
||||||
isThumb: false,
|
asset.id,
|
||||||
isWeb: false,
|
isThumb: false,
|
||||||
);
|
isWeb: false,
|
||||||
|
);
|
||||||
|
|
||||||
final AssetEntity? entity;
|
var motionReponse = await _apiService.assetApi.downloadFileWithHttpInfo(
|
||||||
|
asset.livePhotoVideoId!,
|
||||||
|
isThumb: false,
|
||||||
|
isWeb: false,
|
||||||
|
);
|
||||||
|
|
||||||
if (asset.type == AssetTypeEnum.IMAGE) {
|
final AssetEntity? entity;
|
||||||
entity = await PhotoManager.editor.saveImage(
|
|
||||||
res.bodyBytes,
|
final tempDir = await getTemporaryDirectory();
|
||||||
|
File videoFile = await File('${tempDir.path}/livephoto.mov').create();
|
||||||
|
File imageFile = await File('${tempDir.path}/livephoto.heic').create();
|
||||||
|
videoFile.writeAsBytesSync(motionReponse.bodyBytes);
|
||||||
|
imageFile.writeAsBytesSync(imageResponse.bodyBytes);
|
||||||
|
|
||||||
|
entity = await PhotoManager.editor.darwin.saveLivePhoto(
|
||||||
|
imageFile: imageFile,
|
||||||
|
videoFile: videoFile,
|
||||||
title: p.basename(asset.originalPath),
|
title: p.basename(asset.originalPath),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
final tempDir = await getTemporaryDirectory();
|
|
||||||
File tempFile = await File('${tempDir.path}/$fileName').create();
|
|
||||||
tempFile.writeAsBytesSync(res.bodyBytes);
|
|
||||||
entity = await PhotoManager.editor.saveVideo(tempFile, title: fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entity != null;
|
return entity != null;
|
||||||
|
} else {
|
||||||
|
var res = await _apiService.assetApi.downloadFileWithHttpInfo(
|
||||||
|
asset.id,
|
||||||
|
isThumb: false,
|
||||||
|
isWeb: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
final AssetEntity? entity;
|
||||||
|
|
||||||
|
if (asset.type == AssetTypeEnum.IMAGE) {
|
||||||
|
entity = await PhotoManager.editor.saveImage(
|
||||||
|
res.bodyBytes,
|
||||||
|
title: p.basename(asset.originalPath),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final tempDir = await getTemporaryDirectory();
|
||||||
|
File tempFile = await File('${tempDir.path}/$fileName').create();
|
||||||
|
tempFile.writeAsBytesSync(res.bodyBytes);
|
||||||
|
entity =
|
||||||
|
await PhotoManager.editor.saveVideo(tempFile, title: fileName);
|
||||||
|
}
|
||||||
|
return entity != null;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("Error saving file $e");
|
debugPrint("Error saving file $e");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
|
||||||
|
|
||||||
class DownloadLoadingIndicator extends StatelessWidget {
|
|
||||||
const DownloadLoadingIndicator({
|
|
||||||
Key? key,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
height: 60,
|
|
||||||
width: 60,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).primaryColor,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: const SpinKitDancingSquare(
|
|
||||||
color: Colors.white,
|
|
||||||
size: 30.0,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,7 @@ class _RemotePhotoViewState extends State<RemotePhotoView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleSwipUpDown(PointerMoveEvent details) {
|
void handleSwipUpDown(PointerMoveEvent details) {
|
||||||
int sensitivity = 10;
|
int sensitivity = 15;
|
||||||
|
|
||||||
if (_zoomedIn) {
|
if (_zoomedIn) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -3,21 +3,23 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/shared/models/asset.dart';
|
import 'package:immich_mobile/shared/models/asset.dart';
|
||||||
|
|
||||||
class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget {
|
class TopControlAppBar extends HookConsumerWidget with PreferredSizeWidget {
|
||||||
const TopControlAppBar({
|
const TopControlAppBar({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.asset,
|
required this.asset,
|
||||||
required this.onMoreInfoPressed,
|
required this.onMoreInfoPressed,
|
||||||
required this.onDownloadPressed,
|
required this.onDownloadPressed,
|
||||||
required this.onSharePressed,
|
required this.onSharePressed,
|
||||||
this.loading = false,
|
required this.onToggleMotionVideo,
|
||||||
|
required this.isPlayingMotionVideo,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
final Function onMoreInfoPressed;
|
final Function onMoreInfoPressed;
|
||||||
final VoidCallback? onDownloadPressed;
|
final VoidCallback? onDownloadPressed;
|
||||||
|
final VoidCallback onToggleMotionVideo;
|
||||||
final Function onSharePressed;
|
final Function onSharePressed;
|
||||||
final bool loading;
|
final bool isPlayingMotionVideo;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
@@ -38,14 +40,22 @@ class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
if (loading)
|
if (asset.remote?.livePhotoVideoId != null)
|
||||||
Center(
|
IconButton(
|
||||||
child: Container(
|
iconSize: iconSize,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 15.0),
|
splashRadius: iconSize,
|
||||||
width: iconSize,
|
onPressed: () {
|
||||||
height: iconSize,
|
onToggleMotionVideo();
|
||||||
child: const CircularProgressIndicator(strokeWidth: 2.0),
|
},
|
||||||
),
|
icon: isPlayingMotionVideo
|
||||||
|
? Icon(
|
||||||
|
Icons.motion_photos_pause_outlined,
|
||||||
|
color: Colors.grey[200],
|
||||||
|
)
|
||||||
|
: Icon(
|
||||||
|
Icons.play_circle_outline_rounded,
|
||||||
|
color: Colors.grey[200],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (!asset.isLocal)
|
if (!asset.isLocal)
|
||||||
IconButton(
|
IconButton(
|
||||||
@@ -79,7 +89,7 @@ class TopControlAppBar extends ConsumerWidget with PreferredSizeWidget {
|
|||||||
Icons.more_horiz_rounded,
|
Icons.more_horiz_rounded,
|
||||||
color: Colors.grey[200],
|
color: Colors.grey[200],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:flutter_swipe_detector/flutter_swipe_detector.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/hive_box.dart';
|
import 'package:immich_mobile/constants/hive_box.dart';
|
||||||
@@ -34,10 +33,10 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||||||
final Box<dynamic> box = Hive.box(userInfoBox);
|
final Box<dynamic> box = Hive.box(userInfoBox);
|
||||||
final appSettingService = ref.watch(appSettingsServiceProvider);
|
final appSettingService = ref.watch(appSettingsServiceProvider);
|
||||||
final threeStageLoading = useState(false);
|
final threeStageLoading = useState(false);
|
||||||
final loading = useState(false);
|
|
||||||
final isZoomed = useState<bool>(false);
|
final isZoomed = useState<bool>(false);
|
||||||
ValueNotifier<bool> isZoomedListener = ValueNotifier<bool>(false);
|
|
||||||
final indexOfAsset = useState(assetList.indexOf(asset));
|
final indexOfAsset = useState(assetList.indexOf(asset));
|
||||||
|
final isPlayingMotionVideo = useState(false);
|
||||||
|
ValueNotifier<bool> isZoomedListener = ValueNotifier<bool>(false);
|
||||||
|
|
||||||
PageController controller =
|
PageController controller =
|
||||||
PageController(initialPage: assetList.indexOf(asset));
|
PageController(initialPage: assetList.indexOf(asset));
|
||||||
@@ -46,6 +45,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||||||
() {
|
() {
|
||||||
threeStageLoading.value = appSettingService
|
threeStageLoading.value = appSettingService
|
||||||
.getSetting<bool>(AppSettingsEnum.threeStageLoading);
|
.getSetting<bool>(AppSettingsEnum.threeStageLoading);
|
||||||
|
isPlayingMotionVideo.value = false;
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
@@ -86,7 +86,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
appBar: TopControlAppBar(
|
appBar: TopControlAppBar(
|
||||||
loading: loading.value,
|
isPlayingMotionVideo: isPlayingMotionVideo.value,
|
||||||
asset: assetList[indexOfAsset.value],
|
asset: assetList[indexOfAsset.value],
|
||||||
onMoreInfoPressed: () {
|
onMoreInfoPressed: () {
|
||||||
showInfo();
|
showInfo();
|
||||||
@@ -95,13 +95,18 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
ref.watch(imageViewerStateProvider.notifier).downloadAsset(
|
ref.watch(imageViewerStateProvider.notifier).downloadAsset(
|
||||||
assetList[indexOfAsset.value].remote!, context);
|
assetList[indexOfAsset.value].remote!,
|
||||||
|
context,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
onSharePressed: () {
|
onSharePressed: () {
|
||||||
ref
|
ref
|
||||||
.watch(imageViewerStateProvider.notifier)
|
.watch(imageViewerStateProvider.notifier)
|
||||||
.shareAsset(assetList[indexOfAsset.value], context);
|
.shareAsset(assetList[indexOfAsset.value], context);
|
||||||
},
|
},
|
||||||
|
onToggleMotionVideo: (() {
|
||||||
|
isPlayingMotionVideo.value = !isPlayingMotionVideo.value;
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: PageView.builder(
|
child: PageView.builder(
|
||||||
@@ -120,25 +125,43 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||||||
getAssetExif();
|
getAssetExif();
|
||||||
|
|
||||||
if (assetList[index].isImage) {
|
if (assetList[index].isImage) {
|
||||||
return ImageViewerPage(
|
if (isPlayingMotionVideo.value) {
|
||||||
authToken: 'Bearer ${box.get(accessTokenKey)}',
|
return VideoViewerPage(
|
||||||
isZoomedFunction: isZoomedMethod,
|
asset: assetList[index],
|
||||||
isZoomedListener: isZoomedListener,
|
isMotionVideo: true,
|
||||||
asset: assetList[index],
|
onVideoEnded: () {
|
||||||
heroTag: assetList[index].id,
|
isPlayingMotionVideo.value = false;
|
||||||
threeStageLoading: threeStageLoading.value,
|
},
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return ImageViewerPage(
|
||||||
|
authToken: 'Bearer ${box.get(accessTokenKey)}',
|
||||||
|
isZoomedFunction: isZoomedMethod,
|
||||||
|
isZoomedListener: isZoomedListener,
|
||||||
|
asset: assetList[index],
|
||||||
|
heroTag: assetList[index].id,
|
||||||
|
threeStageLoading: threeStageLoading.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return SwipeDetector(
|
return GestureDetector(
|
||||||
onSwipeDown: (_) {
|
onVerticalDragUpdate: (details) {
|
||||||
AutoRouter.of(context).pop();
|
const int sensitivity = 15;
|
||||||
},
|
if (details.delta.dy > sensitivity) {
|
||||||
onSwipeUp: (_) {
|
// swipe down
|
||||||
showInfo();
|
AutoRouter.of(context).pop();
|
||||||
|
} else if (details.delta.dy < -sensitivity) {
|
||||||
|
// swipe up
|
||||||
|
showInfo();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Hero(
|
child: Hero(
|
||||||
tag: assetList[index].id,
|
tag: assetList[index].id,
|
||||||
child: VideoViewerPage(asset: assetList[index]),
|
child: VideoViewerPage(
|
||||||
|
asset: assetList[index],
|
||||||
|
isMotionVideo: false,
|
||||||
|
onVideoEnded: () {},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart';
|
import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart';
|
||||||
import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_state.provider.dart';
|
import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_state.provider.dart';
|
||||||
import 'package:immich_mobile/modules/asset_viewer/ui/download_loading_indicator.dart';
|
|
||||||
import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart';
|
import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart';
|
||||||
import 'package:immich_mobile/modules/asset_viewer/ui/remote_photo_view.dart';
|
import 'package:immich_mobile/modules/asset_viewer/ui/remote_photo_view.dart';
|
||||||
import 'package:immich_mobile/modules/home/services/asset.service.dart';
|
import 'package:immich_mobile/modules/home/services/asset.service.dart';
|
||||||
import 'package:immich_mobile/shared/models/asset.dart';
|
import 'package:immich_mobile/shared/models/asset.dart';
|
||||||
|
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class ImageViewerPage extends HookConsumerWidget {
|
class ImageViewerPage extends HookConsumerWidget {
|
||||||
@@ -84,7 +84,7 @@ class ImageViewerPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
if (downloadAssetStatus == DownloadAssetStatus.loading)
|
if (downloadAssetStatus == DownloadAssetStatus.loading)
|
||||||
const Center(
|
const Center(
|
||||||
child: DownloadLoadingIndicator(),
|
child: ImmichLoadingIndicator(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,23 +7,34 @@ import 'package:immich_mobile/constants/hive_box.dart';
|
|||||||
import 'package:chewie/chewie.dart';
|
import 'package:chewie/chewie.dart';
|
||||||
import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart';
|
import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart';
|
||||||
import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_state.provider.dart';
|
import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_state.provider.dart';
|
||||||
import 'package:immich_mobile/modules/asset_viewer/ui/download_loading_indicator.dart';
|
|
||||||
import 'package:immich_mobile/shared/models/asset.dart';
|
import 'package:immich_mobile/shared/models/asset.dart';
|
||||||
|
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||||
import 'package:photo_manager/photo_manager.dart';
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class VideoViewerPage extends HookConsumerWidget {
|
class VideoViewerPage extends HookConsumerWidget {
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
|
final bool isMotionVideo;
|
||||||
|
final VoidCallback onVideoEnded;
|
||||||
|
|
||||||
const VideoViewerPage({Key? key, required this.asset}) : super(key: key);
|
const VideoViewerPage({
|
||||||
|
Key? key,
|
||||||
|
required this.asset,
|
||||||
|
required this.isMotionVideo,
|
||||||
|
required this.onVideoEnded,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
if (asset.isLocal) {
|
if (asset.isLocal) {
|
||||||
final AsyncValue<File> videoFile = ref.watch(_fileFamily(asset.local!));
|
final AsyncValue<File> videoFile = ref.watch(_fileFamily(asset.local!));
|
||||||
return videoFile.when(
|
return videoFile.when(
|
||||||
data: (data) => VideoThumbnailPlayer(file: data),
|
data: (data) => VideoThumbnailPlayer(
|
||||||
|
file: data,
|
||||||
|
isMotionVideo: false,
|
||||||
|
onVideoEnded: () {},
|
||||||
|
),
|
||||||
error: (error, stackTrace) => Icon(
|
error: (error, stackTrace) => Icon(
|
||||||
Icons.image_not_supported_outlined,
|
Icons.image_not_supported_outlined,
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
@@ -41,18 +52,21 @@ class VideoViewerPage extends HookConsumerWidget {
|
|||||||
ref.watch(imageViewerStateProvider).downloadAssetStatus;
|
ref.watch(imageViewerStateProvider).downloadAssetStatus;
|
||||||
final box = Hive.box(userInfoBox);
|
final box = Hive.box(userInfoBox);
|
||||||
final String jwtToken = box.get(accessTokenKey);
|
final String jwtToken = box.get(accessTokenKey);
|
||||||
final String videoUrl =
|
final String videoUrl = isMotionVideo
|
||||||
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}';
|
? '${box.get(serverEndpointKey)}/asset/file/${asset.remote?.livePhotoVideoId!}'
|
||||||
|
: '${box.get(serverEndpointKey)}/asset/file/${asset.id}';
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
VideoThumbnailPlayer(
|
VideoThumbnailPlayer(
|
||||||
url: videoUrl,
|
url: videoUrl,
|
||||||
jwtToken: jwtToken,
|
jwtToken: jwtToken,
|
||||||
|
isMotionVideo: isMotionVideo,
|
||||||
|
onVideoEnded: onVideoEnded,
|
||||||
),
|
),
|
||||||
if (downloadAssetStatus == DownloadAssetStatus.loading)
|
if (downloadAssetStatus == DownloadAssetStatus.loading)
|
||||||
const Center(
|
const Center(
|
||||||
child: DownloadLoadingIndicator(),
|
child: ImmichLoadingIndicator(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -72,9 +86,17 @@ class VideoThumbnailPlayer extends StatefulWidget {
|
|||||||
final String? url;
|
final String? url;
|
||||||
final String? jwtToken;
|
final String? jwtToken;
|
||||||
final File? file;
|
final File? file;
|
||||||
|
final bool isMotionVideo;
|
||||||
|
final VoidCallback onVideoEnded;
|
||||||
|
|
||||||
const VideoThumbnailPlayer({Key? key, this.url, this.jwtToken, this.file})
|
const VideoThumbnailPlayer({
|
||||||
: super(key: key);
|
Key? key,
|
||||||
|
this.url,
|
||||||
|
this.jwtToken,
|
||||||
|
this.file,
|
||||||
|
required this.onVideoEnded,
|
||||||
|
required this.isMotionVideo,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<VideoThumbnailPlayer> createState() => _VideoThumbnailPlayerState();
|
State<VideoThumbnailPlayer> createState() => _VideoThumbnailPlayerState();
|
||||||
@@ -88,6 +110,13 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
initializePlayer();
|
initializePlayer();
|
||||||
|
|
||||||
|
videoPlayerController.addListener(() {
|
||||||
|
if (videoPlayerController.value.position ==
|
||||||
|
videoPlayerController.value.duration) {
|
||||||
|
widget.onVideoEnded();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initializePlayer() async {
|
Future<void> initializePlayer() async {
|
||||||
@@ -115,7 +144,7 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
|
|||||||
autoPlay: true,
|
autoPlay: true,
|
||||||
autoInitialize: true,
|
autoInitialize: true,
|
||||||
allowFullScreen: true,
|
allowFullScreen: true,
|
||||||
showControls: true,
|
showControls: !widget.isMotionVideo,
|
||||||
hideControlsTimer: const Duration(seconds: 5),
|
hideControlsTimer: const Duration(seconds: 5),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:photo_manager/photo_manager.dart';
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
|
|
||||||
class ErrorUploadAsset extends Equatable {
|
class ErrorUploadAsset {
|
||||||
final String id;
|
final String id;
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
final String fileName;
|
final String fileName;
|
||||||
@@ -42,14 +41,25 @@ class ErrorUploadAsset extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props {
|
bool operator ==(Object other) {
|
||||||
return [
|
if (identical(this, other)) return true;
|
||||||
id,
|
|
||||||
createdAt,
|
return other is ErrorUploadAsset &&
|
||||||
fileName,
|
other.id == id &&
|
||||||
fileType,
|
other.createdAt == createdAt &&
|
||||||
asset,
|
other.fileName == fileName &&
|
||||||
errorMessage,
|
other.fileType == fileType &&
|
||||||
];
|
other.asset == asset &&
|
||||||
|
other.errorMessage == errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return id.hashCode ^
|
||||||
|
createdAt.hashCode ^
|
||||||
|
fileName.hashCode ^
|
||||||
|
fileType.hashCode ^
|
||||||
|
asset.hashCode ^
|
||||||
|
errorMessage.hashCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cancellation_token_http/http.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@@ -263,6 +264,13 @@ class BackupService {
|
|||||||
|
|
||||||
req.files.add(assetRawUploadData);
|
req.files.add(assetRawUploadData);
|
||||||
|
|
||||||
|
if (entity.isLivePhoto) {
|
||||||
|
var livePhotoRawUploadData = await _getLivePhotoFile(entity);
|
||||||
|
if (livePhotoRawUploadData != null) {
|
||||||
|
req.files.add(livePhotoRawUploadData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setCurrentUploadAssetCb(
|
setCurrentUploadAssetCb(
|
||||||
CurrentUploadAsset(
|
CurrentUploadAsset(
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
@@ -322,6 +330,33 @@ class BackupService {
|
|||||||
return !anyErrors;
|
return !anyErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<MultipartFile?> _getLivePhotoFile(AssetEntity entity) async {
|
||||||
|
var motionFilePath = await entity.getMediaUrl();
|
||||||
|
// var motionFilePath = '/var/mobile/Media/DCIM/103APPLE/IMG_3371.MOV'
|
||||||
|
|
||||||
|
if (motionFilePath != null) {
|
||||||
|
var validPath = motionFilePath.replaceAll('file://', '');
|
||||||
|
var motionFile = File(validPath);
|
||||||
|
var fileStream = motionFile.openRead();
|
||||||
|
String originalFileName = await entity.titleAsync;
|
||||||
|
String fileNameWithoutPath = originalFileName.toString().split(".")[0];
|
||||||
|
var mimeType = FileHelper.getMimeType(validPath);
|
||||||
|
|
||||||
|
return http.MultipartFile(
|
||||||
|
"livePhotoData",
|
||||||
|
fileStream,
|
||||||
|
motionFile.lengthSync(),
|
||||||
|
filename: fileNameWithoutPath,
|
||||||
|
contentType: MediaType(
|
||||||
|
mimeType["type"],
|
||||||
|
mimeType["subType"],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
String _getAssetType(AssetType assetType) {
|
String _getAssetType(AssetType assetType) {
|
||||||
switch (assetType) {
|
switch (assetType) {
|
||||||
case AssetType.audio:
|
case AssetType.audio:
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
|||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/shared/providers/websocket.provider.dart';
|
import 'package:immich_mobile/shared/providers/websocket.provider.dart';
|
||||||
import 'package:immich_mobile/modules/backup/ui/backup_info_card.dart';
|
import 'package:immich_mobile/modules/backup/ui/backup_info_card.dart';
|
||||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class BackupControllerPage extends HookConsumerWidget {
|
class BackupControllerPage extends HookConsumerWidget {
|
||||||
@@ -63,14 +62,11 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: LinearPercentIndicator(
|
child: LinearProgressIndicator(
|
||||||
padding:
|
minHeight: 10.0,
|
||||||
const EdgeInsets.symmetric(horizontal: 0, vertical: 0),
|
value: backupState.serverInfo.diskUsagePercentage / 100.0,
|
||||||
barRadius: const Radius.circular(2),
|
|
||||||
lineHeight: 10.0,
|
|
||||||
percent: backupState.serverInfo.diskUsagePercentage / 100.0,
|
|
||||||
backgroundColor: Colors.grey,
|
backgroundColor: Colors.grey,
|
||||||
progressColor: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -444,17 +440,21 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: LinearPercentIndicator(
|
child: Row(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 0),
|
children: [
|
||||||
barRadius: const Radius.circular(2),
|
Expanded(
|
||||||
lineHeight: 10.0,
|
child: LinearProgressIndicator(
|
||||||
trailing: Text(
|
minHeight: 10.0,
|
||||||
" ${backupState.progressInPercentage.toStringAsFixed(0)}%",
|
value: backupState.progressInPercentage / 100.0,
|
||||||
style: const TextStyle(fontSize: 12),
|
backgroundColor: Colors.grey,
|
||||||
),
|
color: Theme.of(context).primaryColor,
|
||||||
percent: backupState.progressInPercentage / 100.0,
|
),
|
||||||
backgroundColor: Colors.grey,
|
),
|
||||||
progressColor: Theme.of(context).primaryColor,
|
Text(
|
||||||
|
" ${backupState.progressInPercentage.toStringAsFixed(0)}%",
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
|||||||
@@ -5,21 +5,25 @@ part 'hive_saved_login_info.model.g.dart';
|
|||||||
@HiveType(typeId: 0)
|
@HiveType(typeId: 0)
|
||||||
class HiveSavedLoginInfo {
|
class HiveSavedLoginInfo {
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
String email;
|
String email; // DEPRECATED
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
String password;
|
String password; // DEPRECATED
|
||||||
|
|
||||||
@HiveField(2)
|
@HiveField(2)
|
||||||
String serverUrl;
|
String serverUrl;
|
||||||
|
|
||||||
@HiveField(3)
|
@HiveField(3, defaultValue: false)
|
||||||
bool isSaveLogin;
|
bool isSaveLogin;
|
||||||
|
|
||||||
|
@HiveField(4, defaultValue: "")
|
||||||
|
String accessToken;
|
||||||
|
|
||||||
HiveSavedLoginInfo({
|
HiveSavedLoginInfo({
|
||||||
required this.email,
|
required this.email,
|
||||||
required this.password,
|
required this.password,
|
||||||
required this.serverUrl,
|
required this.serverUrl,
|
||||||
required this.isSaveLogin,
|
required this.isSaveLogin,
|
||||||
|
required this.accessToken,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,14 +20,15 @@ class HiveSavedLoginInfoAdapter extends TypeAdapter<HiveSavedLoginInfo> {
|
|||||||
email: fields[0] as String,
|
email: fields[0] as String,
|
||||||
password: fields[1] as String,
|
password: fields[1] as String,
|
||||||
serverUrl: fields[2] as String,
|
serverUrl: fields[2] as String,
|
||||||
isSaveLogin: fields[3] as bool,
|
isSaveLogin: fields[3] == null ? false : fields[3] as bool,
|
||||||
|
accessToken: fields[4] == null ? '' : fields[4] as String,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, HiveSavedLoginInfo obj) {
|
void write(BinaryWriter writer, HiveSavedLoginInfo obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(4)
|
..writeByte(5)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
..write(obj.email)
|
..write(obj.email)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
@@ -35,7 +36,9 @@ class HiveSavedLoginInfoAdapter extends TypeAdapter<HiveSavedLoginInfo> {
|
|||||||
..writeByte(2)
|
..writeByte(2)
|
||||||
..write(obj.serverUrl)
|
..write(obj.serverUrl)
|
||||||
..writeByte(3)
|
..writeByte(3)
|
||||||
..write(obj.isSaveLogin);
|
..write(obj.isSaveLogin)
|
||||||
|
..writeByte(4)
|
||||||
|
..write(obj.accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -74,15 +74,6 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store device id to local storage
|
|
||||||
var deviceInfo = await _deviceInfoService.getDeviceInfo();
|
|
||||||
Hive.box(userInfoBox).put(deviceIdKey, deviceInfo["deviceId"]);
|
|
||||||
|
|
||||||
state = state.copyWith(
|
|
||||||
deviceId: deviceInfo["deviceId"],
|
|
||||||
deviceType: deviceInfo["deviceType"],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Make sign-in request
|
// Make sign-in request
|
||||||
try {
|
try {
|
||||||
var loginResponse = await _apiService.authenticationApi.login(
|
var loginResponse = await _apiService.authenticationApi.login(
|
||||||
@@ -97,65 +88,15 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Hive.box(userInfoBox).put(accessTokenKey, loginResponse.accessToken);
|
return setSuccessLoginInfo(
|
||||||
|
accessToken: loginResponse.accessToken,
|
||||||
state = state.copyWith(
|
isSavedLoginInfo: isSavedLoginInfo,
|
||||||
isAuthenticated: true,
|
|
||||||
userId: loginResponse.userId,
|
|
||||||
userEmail: loginResponse.userEmail,
|
|
||||||
firstName: loginResponse.firstName,
|
|
||||||
lastName: loginResponse.lastName,
|
|
||||||
profileImagePath: loginResponse.profileImagePath,
|
|
||||||
isAdmin: loginResponse.isAdmin,
|
|
||||||
shouldChangePassword: loginResponse.shouldChangePassword,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Login Success - Set Access Token to API Client
|
|
||||||
_apiService.setAccessToken(loginResponse.accessToken);
|
|
||||||
|
|
||||||
if (isSavedLoginInfo) {
|
|
||||||
// Save login info to local storage
|
|
||||||
Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).put(
|
|
||||||
savedLoginInfoKey,
|
|
||||||
HiveSavedLoginInfo(
|
|
||||||
email: email,
|
|
||||||
password: password,
|
|
||||||
isSaveLogin: true,
|
|
||||||
serverUrl: Hive.box(userInfoBox).get(serverEndpointKey),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox)
|
|
||||||
.delete(savedLoginInfoKey);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
HapticFeedback.vibrate();
|
HapticFeedback.vibrate();
|
||||||
debugPrint("Error logging in $e");
|
debugPrint("Error logging in $e");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register device info
|
|
||||||
try {
|
|
||||||
DeviceInfoResponseDto? deviceInfo =
|
|
||||||
await _apiService.deviceInfoApi.createDeviceInfo(
|
|
||||||
CreateDeviceInfoDto(
|
|
||||||
deviceId: state.deviceId,
|
|
||||||
deviceType: state.deviceType,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (deviceInfo == null) {
|
|
||||||
debugPrint('Device Info Response is null');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = state.copyWith(deviceInfo: deviceInfo);
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("ERROR Register Device Info: $e");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> logout() async {
|
Future<bool> logout() async {
|
||||||
@@ -215,6 +156,74 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> setSuccessLoginInfo({
|
||||||
|
required String accessToken,
|
||||||
|
required bool isSavedLoginInfo,
|
||||||
|
}) async {
|
||||||
|
Hive.box(userInfoBox).put(accessTokenKey, accessToken);
|
||||||
|
|
||||||
|
_apiService.setAccessToken(accessToken);
|
||||||
|
var userResponseDto = await _apiService.userApi.getMyUserInfo();
|
||||||
|
|
||||||
|
if (userResponseDto != null) {
|
||||||
|
var deviceInfo = await _deviceInfoService.getDeviceInfo();
|
||||||
|
Hive.box(userInfoBox).put(deviceIdKey, deviceInfo["deviceId"]);
|
||||||
|
|
||||||
|
state = state.copyWith(
|
||||||
|
isAuthenticated: true,
|
||||||
|
userId: userResponseDto.id,
|
||||||
|
userEmail: userResponseDto.email,
|
||||||
|
firstName: userResponseDto.firstName,
|
||||||
|
lastName: userResponseDto.lastName,
|
||||||
|
profileImagePath: userResponseDto.profileImagePath,
|
||||||
|
isAdmin: userResponseDto.isAdmin,
|
||||||
|
shouldChangePassword: userResponseDto.shouldChangePassword,
|
||||||
|
deviceId: deviceInfo["deviceId"],
|
||||||
|
deviceType: deviceInfo["deviceType"],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isSavedLoginInfo) {
|
||||||
|
// Save login info to local storage
|
||||||
|
Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).put(
|
||||||
|
savedLoginInfoKey,
|
||||||
|
HiveSavedLoginInfo(
|
||||||
|
email: "",
|
||||||
|
password: "",
|
||||||
|
isSaveLogin: true,
|
||||||
|
serverUrl: Hive.box(userInfoBox).get(serverEndpointKey),
|
||||||
|
accessToken: accessToken,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox)
|
||||||
|
.delete(savedLoginInfoKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register device info
|
||||||
|
try {
|
||||||
|
DeviceInfoResponseDto? deviceInfo =
|
||||||
|
await _apiService.deviceInfoApi.createDeviceInfo(
|
||||||
|
CreateDeviceInfoDto(
|
||||||
|
deviceId: state.deviceId,
|
||||||
|
deviceType: state.deviceType,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (deviceInfo == null) {
|
||||||
|
debugPrint('Device Info Response is null');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = state.copyWith(deviceInfo: deviceInfo);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("ERROR Register Device Info: $e");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final authenticationProvider =
|
final authenticationProvider =
|
||||||
|
|||||||
6
mobile/lib/modules/login/providers/oauth.provider.dart
Normal file
6
mobile/lib/modules/login/providers/oauth.provider.dart
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/modules/login/services/oauth.service.dart';
|
||||||
|
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||||
|
|
||||||
|
final OAuthServiceProvider =
|
||||||
|
Provider((ref) => OAuthService(ref.watch(apiServiceProvider)));
|
||||||
39
mobile/lib/modules/login/services/oauth.service.dart
Normal file
39
mobile/lib/modules/login/services/oauth.service.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
import 'package:flutter_web_auth/flutter_web_auth.dart';
|
||||||
|
|
||||||
|
// Redirect URL = app.immich://
|
||||||
|
|
||||||
|
class OAuthService {
|
||||||
|
final ApiService _apiService;
|
||||||
|
final callbackUrlScheme = 'app.immich';
|
||||||
|
|
||||||
|
OAuthService(this._apiService);
|
||||||
|
|
||||||
|
Future<OAuthConfigResponseDto?> getOAuthServerConfig(
|
||||||
|
String serverEndpoint,
|
||||||
|
) async {
|
||||||
|
_apiService.setEndpoint(serverEndpoint);
|
||||||
|
|
||||||
|
return await _apiService.oAuthApi.generateConfig(
|
||||||
|
OAuthConfigDto(redirectUri: '$callbackUrlScheme:/'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<LoginResponseDto?> oAuthLogin(String oauthUrl) async {
|
||||||
|
try {
|
||||||
|
var result = await FlutterWebAuth.authenticate(
|
||||||
|
url: oauthUrl,
|
||||||
|
callbackUrlScheme: callbackUrlScheme,
|
||||||
|
);
|
||||||
|
|
||||||
|
return await _apiService.oAuthApi.callback(
|
||||||
|
OAuthCallbackDto(
|
||||||
|
url: result,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,11 +6,14 @@ import 'package:hive/hive.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/hive_box.dart';
|
import 'package:immich_mobile/constants/hive_box.dart';
|
||||||
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/login/providers/oauth.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||||
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
||||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
class LoginForm extends HookConsumerWidget {
|
class LoginForm extends HookConsumerWidget {
|
||||||
const LoginForm({Key? key}) : super(key: key);
|
const LoginForm({Key? key}) : super(key: key);
|
||||||
@@ -23,10 +26,47 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
useTextEditingController.fromValue(TextEditingValue.empty);
|
useTextEditingController.fromValue(TextEditingValue.empty);
|
||||||
final serverEndpointController =
|
final serverEndpointController =
|
||||||
useTextEditingController(text: 'login_form_endpoint_hint'.tr());
|
useTextEditingController(text: 'login_form_endpoint_hint'.tr());
|
||||||
|
final apiService = ref.watch(apiServiceProvider);
|
||||||
|
final serverEndpointFocusNode = useFocusNode();
|
||||||
final isSaveLoginInfo = useState<bool>(false);
|
final isSaveLoginInfo = useState<bool>(false);
|
||||||
|
final isLoading = useState<bool>(false);
|
||||||
|
final isOauthEnable = useState<bool>(false);
|
||||||
|
final oAuthButtonLabel = useState<String>('OAuth');
|
||||||
|
|
||||||
|
getServeLoginConfig() async {
|
||||||
|
if (!serverEndpointFocusNode.hasFocus) {
|
||||||
|
var urlText = serverEndpointController.text.trim();
|
||||||
|
|
||||||
|
try {
|
||||||
|
var endpointUrl = Uri.tryParse(urlText);
|
||||||
|
|
||||||
|
if (endpointUrl != null) {
|
||||||
|
isLoading.value = true;
|
||||||
|
apiService.setEndpoint(endpointUrl.toString());
|
||||||
|
var loginConfig = await apiService.oAuthApi.generateConfig(
|
||||||
|
OAuthConfigDto(redirectUri: endpointUrl.toString()),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (loginConfig != null) {
|
||||||
|
isOauthEnable.value = loginConfig.enabled;
|
||||||
|
oAuthButtonLabel.value = loginConfig.buttonText ?? 'OAuth';
|
||||||
|
} else {
|
||||||
|
isOauthEnable.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
isLoading.value = false;
|
||||||
|
isOauthEnable.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() {
|
() {
|
||||||
|
serverEndpointFocusNode.addListener(getServeLoginConfig);
|
||||||
|
|
||||||
var loginInfo = Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox)
|
var loginInfo = Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox)
|
||||||
.get(savedLoginInfoKey);
|
.get(savedLoginInfoKey);
|
||||||
|
|
||||||
@@ -37,6 +77,7 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
isSaveLoginInfo.value = loginInfo.isSaveLogin;
|
isSaveLoginInfo.value = loginInfo.isSaveLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getServeLoginConfig();
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
@@ -67,7 +108,10 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
EmailInput(controller: usernameController),
|
EmailInput(controller: usernameController),
|
||||||
PasswordInput(controller: passwordController),
|
PasswordInput(controller: passwordController),
|
||||||
ServerEndpointInput(controller: serverEndpointController),
|
ServerEndpointInput(
|
||||||
|
controller: serverEndpointController,
|
||||||
|
focusNode: serverEndpointFocusNode,
|
||||||
|
),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
activeColor: Theme.of(context).primaryColor,
|
activeColor: Theme.of(context).primaryColor,
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
@@ -92,12 +136,52 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LoginButton(
|
if (isLoading.value)
|
||||||
emailController: usernameController,
|
const SizedBox(
|
||||||
passwordController: passwordController,
|
width: 24,
|
||||||
serverEndpointController: serverEndpointController,
|
height: 24,
|
||||||
isSavedLoginInfo: isSaveLoginInfo.value,
|
child: CircularProgressIndicator(
|
||||||
),
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!isLoading.value)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
LoginButton(
|
||||||
|
emailController: usernameController,
|
||||||
|
passwordController: passwordController,
|
||||||
|
serverEndpointController: serverEndpointController,
|
||||||
|
isSavedLoginInfo: isSaveLoginInfo.value,
|
||||||
|
),
|
||||||
|
if (isOauthEnable.value) ...[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
),
|
||||||
|
child: Divider(
|
||||||
|
color: Brightness.dark == Theme.of(context).brightness
|
||||||
|
? Colors.white
|
||||||
|
: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
OAuthLoginButton(
|
||||||
|
serverEndpointController: serverEndpointController,
|
||||||
|
isSavedLoginInfo: isSaveLoginInfo.value,
|
||||||
|
buttonLabel: oAuthButtonLabel.value,
|
||||||
|
isLoading: isLoading,
|
||||||
|
onLoginSuccess: () {
|
||||||
|
isLoading.value = false;
|
||||||
|
ref.watch(backupProvider.notifier).resumeBackup();
|
||||||
|
AutoRouter.of(context).replace(
|
||||||
|
const TabControllerRoute(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -108,9 +192,12 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
|
|
||||||
class ServerEndpointInput extends StatelessWidget {
|
class ServerEndpointInput extends StatelessWidget {
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
|
final FocusNode focusNode;
|
||||||
const ServerEndpointInput({Key? key, required this.controller})
|
const ServerEndpointInput({
|
||||||
: super(key: key);
|
Key? key,
|
||||||
|
required this.controller,
|
||||||
|
required this.focusNode,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
String? _validateInput(String? url) {
|
String? _validateInput(String? url) {
|
||||||
if (url?.startsWith(RegExp(r'https?://')) == true) {
|
if (url?.startsWith(RegExp(r'https?://')) == true) {
|
||||||
@@ -131,6 +218,7 @@ class ServerEndpointInput extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
validator: _validateInput,
|
validator: _validateInput,
|
||||||
autovalidateMode: AutovalidateMode.always,
|
autovalidateMode: AutovalidateMode.always,
|
||||||
|
focusNode: focusNode,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,13 +288,9 @@ class LoginButton extends ConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
return ElevatedButton(
|
return ElevatedButton.icon(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
visualDensity: VisualDensity.standard,
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
|
||||||
foregroundColor: Colors.grey[50],
|
|
||||||
elevation: 2,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
|
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
// This will remove current cache asset state of previous user login.
|
// This will remove current cache asset state of previous user login.
|
||||||
@@ -238,10 +322,101 @@ class LoginButton extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: const Text(
|
icon: const Icon(Icons.login_rounded),
|
||||||
|
label: const Text(
|
||||||
"login_form_button_text",
|
"login_form_button_text",
|
||||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
).tr(),
|
).tr(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class OAuthLoginButton extends ConsumerWidget {
|
||||||
|
final TextEditingController serverEndpointController;
|
||||||
|
final bool isSavedLoginInfo;
|
||||||
|
final ValueNotifier<bool> isLoading;
|
||||||
|
final VoidCallback onLoginSuccess;
|
||||||
|
final String buttonLabel;
|
||||||
|
|
||||||
|
const OAuthLoginButton({
|
||||||
|
Key? key,
|
||||||
|
required this.serverEndpointController,
|
||||||
|
required this.isSavedLoginInfo,
|
||||||
|
required this.isLoading,
|
||||||
|
required this.onLoginSuccess,
|
||||||
|
required this.buttonLabel,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
var oAuthService = ref.watch(OAuthServiceProvider);
|
||||||
|
|
||||||
|
void performOAuthLogin() async {
|
||||||
|
ref.watch(assetProvider.notifier).clearAllAsset();
|
||||||
|
OAuthConfigResponseDto? oAuthServerConfig;
|
||||||
|
|
||||||
|
try {
|
||||||
|
oAuthServerConfig = await oAuthService
|
||||||
|
.getOAuthServerConfig(serverEndpointController.text);
|
||||||
|
|
||||||
|
isLoading.value = true;
|
||||||
|
} catch (e) {
|
||||||
|
ImmichToast.show(
|
||||||
|
context: context,
|
||||||
|
msg: "login_form_failed_get_oauth_server_config".tr(),
|
||||||
|
toastType: ToastType.error,
|
||||||
|
);
|
||||||
|
isLoading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oAuthServerConfig != null && oAuthServerConfig.enabled) {
|
||||||
|
var loginResponseDto =
|
||||||
|
await oAuthService.oAuthLogin(oAuthServerConfig.url!);
|
||||||
|
|
||||||
|
if (loginResponseDto != null) {
|
||||||
|
var isSuccess = await ref
|
||||||
|
.watch(authenticationProvider.notifier)
|
||||||
|
.setSuccessLoginInfo(
|
||||||
|
accessToken: loginResponseDto.accessToken,
|
||||||
|
isSavedLoginInfo: isSavedLoginInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
isLoading.value = false;
|
||||||
|
onLoginSuccess();
|
||||||
|
} else {
|
||||||
|
ImmichToast.show(
|
||||||
|
context: context,
|
||||||
|
msg: "login_form_failed_login".tr(),
|
||||||
|
toastType: ToastType.error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false;
|
||||||
|
} else {
|
||||||
|
ImmichToast.show(
|
||||||
|
context: context,
|
||||||
|
msg: "login_form_failed_get_oauth_server_disable".tr(),
|
||||||
|
toastType: ToastType.info,
|
||||||
|
);
|
||||||
|
isLoading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ElevatedButton.icon(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).primaryColor.withAlpha(230),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
|
),
|
||||||
|
onPressed: performOAuthLogin,
|
||||||
|
icon: const Icon(Icons.pin_rounded),
|
||||||
|
label: Text(
|
||||||
|
buttonLabel,
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||||
import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
|
import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
|
||||||
@@ -10,6 +9,7 @@ import 'package:immich_mobile/modules/search/providers/search_result_page.provid
|
|||||||
import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
|
import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
|
||||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||||
|
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||||
|
|
||||||
class SearchResultPage extends HookConsumerWidget {
|
class SearchResultPage extends HookConsumerWidget {
|
||||||
const SearchResultPage({Key? key, required this.searchTerm})
|
const SearchResultPage({Key? key, required this.searchTerm})
|
||||||
@@ -122,11 +122,7 @@ class SearchResultPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (searchResultPageState.isLoading) {
|
if (searchResultPageState.isLoading) {
|
||||||
return Center(
|
return const Center(child: ImmichLoadingIndicator());
|
||||||
child: SpinKitDancingSquare(
|
|
||||||
color: Theme.of(context).primaryColor,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchResultPageState.isSuccess) {
|
if (searchResultPageState.isSuccess) {
|
||||||
|
|||||||
@@ -65,7 +65,11 @@ class _$AppRouter extends RootStackRouter {
|
|||||||
final args = routeData.argsAs<VideoViewerRouteArgs>();
|
final args = routeData.argsAs<VideoViewerRouteArgs>();
|
||||||
return MaterialPageX<dynamic>(
|
return MaterialPageX<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
child: VideoViewerPage(key: args.key, asset: args.asset));
|
child: VideoViewerPage(
|
||||||
|
key: args.key,
|
||||||
|
asset: args.asset,
|
||||||
|
isMotionVideo: args.isMotionVideo,
|
||||||
|
onVideoEnded: args.onVideoEnded));
|
||||||
},
|
},
|
||||||
BackupControllerRoute.name: (routeData) {
|
BackupControllerRoute.name: (routeData) {
|
||||||
return MaterialPageX<dynamic>(
|
return MaterialPageX<dynamic>(
|
||||||
@@ -340,24 +344,40 @@ class ImageViewerRouteArgs {
|
|||||||
/// generated route for
|
/// generated route for
|
||||||
/// [VideoViewerPage]
|
/// [VideoViewerPage]
|
||||||
class VideoViewerRoute extends PageRouteInfo<VideoViewerRouteArgs> {
|
class VideoViewerRoute extends PageRouteInfo<VideoViewerRouteArgs> {
|
||||||
VideoViewerRoute({Key? key, required Asset asset})
|
VideoViewerRoute(
|
||||||
|
{Key? key,
|
||||||
|
required Asset asset,
|
||||||
|
required bool isMotionVideo,
|
||||||
|
required void Function() onVideoEnded})
|
||||||
: super(VideoViewerRoute.name,
|
: super(VideoViewerRoute.name,
|
||||||
path: '/video-viewer-page',
|
path: '/video-viewer-page',
|
||||||
args: VideoViewerRouteArgs(key: key, asset: asset));
|
args: VideoViewerRouteArgs(
|
||||||
|
key: key,
|
||||||
|
asset: asset,
|
||||||
|
isMotionVideo: isMotionVideo,
|
||||||
|
onVideoEnded: onVideoEnded));
|
||||||
|
|
||||||
static const String name = 'VideoViewerRoute';
|
static const String name = 'VideoViewerRoute';
|
||||||
}
|
}
|
||||||
|
|
||||||
class VideoViewerRouteArgs {
|
class VideoViewerRouteArgs {
|
||||||
const VideoViewerRouteArgs({this.key, required this.asset});
|
const VideoViewerRouteArgs(
|
||||||
|
{this.key,
|
||||||
|
required this.asset,
|
||||||
|
required this.isMotionVideo,
|
||||||
|
required this.onVideoEnded});
|
||||||
|
|
||||||
final Key? key;
|
final Key? key;
|
||||||
|
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
|
|
||||||
|
final bool isMotionVideo;
|
||||||
|
|
||||||
|
final void Function() onVideoEnded;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'VideoViewerRouteArgs{key: $key, asset: $asset}';
|
return 'VideoViewerRouteArgs{key: $key, asset: $asset, isMotionVideo: $isMotionVideo, onVideoEnded: $onVideoEnded}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
import 'package:immich_mobile/constants/hive_box.dart';
|
import 'package:immich_mobile/constants/hive_box.dart';
|
||||||
import 'package:immich_mobile/shared/views/version_announcement_overlay.dart';
|
import 'package:immich_mobile/shared/views/version_announcement_overlay.dart';
|
||||||
|
|
||||||
@@ -9,21 +11,20 @@ class ReleaseInfoNotifier extends StateNotifier<String> {
|
|||||||
ReleaseInfoNotifier() : super("");
|
ReleaseInfoNotifier() : super("");
|
||||||
|
|
||||||
void checkGithubReleaseInfo() async {
|
void checkGithubReleaseInfo() async {
|
||||||
var dio = Dio();
|
final Client client = Client();
|
||||||
var box = Hive.box(hiveGithubReleaseInfoBox);
|
var box = Hive.box(hiveGithubReleaseInfoBox);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String? localReleaseVersion = box.get(githubReleaseInfoKey);
|
String? localReleaseVersion = box.get(githubReleaseInfoKey);
|
||||||
|
final res = await client.get(
|
||||||
var res = await dio.get(
|
Uri.parse(
|
||||||
"https://api.github.com/repos/alextran1502/immich/releases/latest",
|
"https://api.github.com/repos/immich-app/immich/releases/latest",
|
||||||
options: Options(
|
),
|
||||||
headers: {"Accept": "application/vnd.github.v3+json"},
|
headers: {"Accept": "application/vnd.github.v3+json"});
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
String latestTagVersion = res.data["tag_name"];
|
final data = jsonDecode(res.body);
|
||||||
|
String latestTagVersion = data["tag_name"];
|
||||||
state = latestTagVersion;
|
state = latestTagVersion;
|
||||||
|
|
||||||
debugPrint("Local release version $localReleaseVersion");
|
debugPrint("Local release version $localReleaseVersion");
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ class ApiService {
|
|||||||
|
|
||||||
late UserApi userApi;
|
late UserApi userApi;
|
||||||
late AuthenticationApi authenticationApi;
|
late AuthenticationApi authenticationApi;
|
||||||
|
late OAuthApi oAuthApi;
|
||||||
late AlbumApi albumApi;
|
late AlbumApi albumApi;
|
||||||
late AssetApi assetApi;
|
late AssetApi assetApi;
|
||||||
late ServerInfoApi serverInfoApi;
|
late ServerInfoApi serverInfoApi;
|
||||||
@@ -14,6 +15,7 @@ class ApiService {
|
|||||||
_apiClient = ApiClient(basePath: endpoint);
|
_apiClient = ApiClient(basePath: endpoint);
|
||||||
userApi = UserApi(_apiClient);
|
userApi = UserApi(_apiClient);
|
||||||
authenticationApi = AuthenticationApi(_apiClient);
|
authenticationApi = AuthenticationApi(_apiClient);
|
||||||
|
oAuthApi = OAuthApi(_apiClient);
|
||||||
albumApi = AlbumApi(_apiClient);
|
albumApi = AlbumApi(_apiClient);
|
||||||
assetApi = AssetApi(_apiClient);
|
assetApi = AssetApi(_apiClient);
|
||||||
serverInfoApi = ServerInfoApi(_apiClient);
|
serverInfoApi = ServerInfoApi(_apiClient);
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/constants/hive_box.dart';
|
|
||||||
import 'package:immich_mobile/utils/dio_http_interceptor.dart';
|
|
||||||
|
|
||||||
final networkServiceProvider = Provider((_) => NetworkService());
|
|
||||||
|
|
||||||
class NetworkService {
|
|
||||||
late final Dio dio;
|
|
||||||
|
|
||||||
NetworkService() {
|
|
||||||
dio = Dio();
|
|
||||||
dio.interceptors.add(AuthenticatedRequestInterceptor());
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<dynamic> deleteRequest({required String url, dynamic data}) async {
|
|
||||||
try {
|
|
||||||
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
|
||||||
Response res = await dio.delete('$savedEndpoint/$url', data: data);
|
|
||||||
|
|
||||||
if (res.statusCode == 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
} on DioError catch (e) {
|
|
||||||
debugPrint("DioError: ${e.response}");
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("ERROR deleteRequest: ${e.toString()}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<dynamic> getRequest({
|
|
||||||
required String url,
|
|
||||||
bool isByteResponse = false,
|
|
||||||
bool isStreamReponse = false,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
|
||||||
|
|
||||||
if (isByteResponse) {
|
|
||||||
Response<List<int>> res = await dio.get<List<int>>(
|
|
||||||
'$savedEndpoint/$url',
|
|
||||||
options: Options(responseType: ResponseType.bytes),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (res.statusCode == 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
} else if (isStreamReponse) {
|
|
||||||
Response<ResponseBody> res = await dio.get<ResponseBody>(
|
|
||||||
'$savedEndpoint/$url',
|
|
||||||
options: Options(responseType: ResponseType.stream),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (res.statusCode == 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response res = await dio.get('$savedEndpoint/$url');
|
|
||||||
if (res.statusCode == 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} on DioError catch (e) {
|
|
||||||
debugPrint("DioError: ${e.response}");
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("ERROR getRequest: ${e.toString()}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<dynamic> postRequest({required String url, dynamic data}) async {
|
|
||||||
try {
|
|
||||||
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
|
||||||
var validUrl = Uri.parse('$savedEndpoint/$url').toString();
|
|
||||||
var res = await dio.post(validUrl, data: data);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
} on DioError catch (e) {
|
|
||||||
debugPrint("[postRequest] DioError: ${e.response}");
|
|
||||||
return null;
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("ERROR PostRequest: $e");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<dynamic> putRequest({required String url, dynamic data}) async {
|
|
||||||
try {
|
|
||||||
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
|
||||||
var validUrl = Uri.parse('$savedEndpoint/$url').toString();
|
|
||||||
var res = await dio.put(validUrl, data: data);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
} on DioError catch (e) {
|
|
||||||
debugPrint("DioError: ${e.response}");
|
|
||||||
return null;
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("ERROR PutRequest: $e");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<dynamic> patchRequest({required String url, dynamic data}) async {
|
|
||||||
try {
|
|
||||||
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
|
||||||
var validUrl = Uri.parse('$savedEndpoint/$url').toString();
|
|
||||||
var res = await dio.patch(validUrl, data: data);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
} on DioError catch (e) {
|
|
||||||
debugPrint("DioError: ${e.response}");
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("ERROR PatchRequest: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> pingServer() async {
|
|
||||||
try {
|
|
||||||
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
|
||||||
var validUrl = Uri.parse('$savedEndpoint/server-info/ping').toString();
|
|
||||||
|
|
||||||
debugPrint("ping server at url $validUrl");
|
|
||||||
|
|
||||||
var res = await dio.get(validUrl);
|
|
||||||
var jsonRespsonse = jsonDecode(res.toString());
|
|
||||||
|
|
||||||
return jsonRespsonse["res"] == "pong";
|
|
||||||
} on DioError catch (e) {
|
|
||||||
debugPrint("[PING SERVER] DioError: ${e.response} - $e");
|
|
||||||
return false;
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("ERROR PingServer: $e");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -28,8 +28,7 @@ class ShareService {
|
|||||||
final fileName = basename(asset.remote!.originalPath);
|
final fileName = basename(asset.remote!.originalPath);
|
||||||
final tempFile = await File('${tempDir.path}/$fileName').create();
|
final tempFile = await File('${tempDir.path}/$fileName').create();
|
||||||
final res = await _apiService.assetApi.downloadFileWithHttpInfo(
|
final res = await _apiService.assetApi.downloadFileWithHttpInfo(
|
||||||
asset.remote!.deviceAssetId,
|
asset.remote!.id,
|
||||||
asset.remote!.deviceId,
|
|
||||||
isThumb: false,
|
isThumb: false,
|
||||||
isWeb: false,
|
isWeb: false,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
|
||||||
|
|
||||||
class ImmichLoadingIndicator extends StatelessWidget {
|
class ImmichLoadingIndicator extends StatelessWidget {
|
||||||
const ImmichLoadingIndicator({
|
const ImmichLoadingIndicator({
|
||||||
@@ -15,10 +14,8 @@ class ImmichLoadingIndicator extends StatelessWidget {
|
|||||||
color: Theme.of(context).primaryColor.withAlpha(200),
|
color: Theme.of(context).primaryColor.withAlpha(200),
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: const SpinKitDancingSquare(
|
padding: const EdgeInsets.all(15),
|
||||||
color: Colors.white,
|
child: const CircularProgressIndicator(color: Colors.white),
|
||||||
size: 30.0,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class ImmichToast {
|
|||||||
required String msg,
|
required String msg,
|
||||||
ToastType toastType = ToastType.info,
|
ToastType toastType = ToastType.info,
|
||||||
ToastGravity gravity = ToastGravity.TOP,
|
ToastGravity gravity = ToastGravity.TOP,
|
||||||
|
int durationInSecond = 3,
|
||||||
}) {
|
}) {
|
||||||
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
||||||
final fToast = FToast();
|
final fToast = FToast();
|
||||||
@@ -77,7 +78,7 @@ class ImmichToast {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
gravity: gravity,
|
gravity: gravity,
|
||||||
toastDuration: const Duration(seconds: 2),
|
toastDuration: Duration(seconds: durationInSecond),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,30 +8,34 @@ import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
|||||||
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
||||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||||
|
|
||||||
class SplashScreenPage extends HookConsumerWidget {
|
class SplashScreenPage extends HookConsumerWidget {
|
||||||
const SplashScreenPage({Key? key}) : super(key: key);
|
const SplashScreenPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final apiService = ref.watch(apiServiceProvider);
|
||||||
HiveSavedLoginInfo? loginInfo =
|
HiveSavedLoginInfo? loginInfo =
|
||||||
Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).get(savedLoginInfoKey);
|
Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).get(savedLoginInfoKey);
|
||||||
|
|
||||||
void performLoggingIn() async {
|
void performLoggingIn() async {
|
||||||
var isAuthenticated =
|
if (loginInfo != null) {
|
||||||
await ref.read(authenticationProvider.notifier).login(
|
// Make sure API service is initialized
|
||||||
loginInfo!.email,
|
apiService.setEndpoint(loginInfo.serverUrl);
|
||||||
loginInfo.password,
|
|
||||||
loginInfo.serverUrl,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isAuthenticated) {
|
var isSuccess =
|
||||||
// Resume backup (if enable) then navigate
|
await ref.read(authenticationProvider.notifier).setSuccessLoginInfo(
|
||||||
ref.watch(backupProvider.notifier).resumeBackup();
|
accessToken: loginInfo.accessToken,
|
||||||
AutoRouter.of(context).replace(const TabControllerRoute());
|
isSavedLoginInfo: true,
|
||||||
} else {
|
);
|
||||||
AutoRouter.of(context).replace(const LoginRoute());
|
if (isSuccess) {
|
||||||
|
// Resume backup (if enable) then navigate
|
||||||
|
ref.watch(backupProvider.notifier).resumeBackup();
|
||||||
|
AutoRouter.of(context).replace(const TabControllerRoute());
|
||||||
|
} else {
|
||||||
|
AutoRouter.of(context).replace(const LoginRoute());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class VersionAnnouncementOverlay extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
void goToReleaseNote() async {
|
void goToReleaseNote() async {
|
||||||
final Uri url =
|
final Uri url =
|
||||||
Uri.parse('https://github.com/alextran1502/immich/releases/latest');
|
Uri.parse('https://github.com/immich-app/immich/releases/latest');
|
||||||
await launchUrl(url);
|
await launchUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
|
||||||
import 'package:immich_mobile/constants/hive_box.dart';
|
|
||||||
|
|
||||||
class AuthenticatedRequestInterceptor extends Interceptor {
|
|
||||||
@override
|
|
||||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
|
||||||
// debugPrint('REQUEST[${options.method}] => PATH: ${options.path}');
|
|
||||||
|
|
||||||
var box = Hive.box(userInfoBox);
|
|
||||||
|
|
||||||
options.headers["Authorization"] = "Bearer ${box.get(accessTokenKey)}";
|
|
||||||
options.responseType = ResponseType.plain;
|
|
||||||
return super.onRequest(options, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,7 @@ String getAlbumThumbnailUrl(
|
|||||||
|
|
||||||
String getImageUrl(final AssetResponseDto asset) {
|
String getImageUrl(final AssetResponseDto asset) {
|
||||||
final box = Hive.box(userInfoBox);
|
final box = Hive.box(userInfoBox);
|
||||||
return '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=false';
|
return '${box.get(serverEndpointKey)}/asset/file/${asset.id}?isThumb=false';
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getThumbnailUrl(
|
String _getThumbnailUrl(
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ doc/JobStatusResponseDto.md
|
|||||||
doc/LoginCredentialDto.md
|
doc/LoginCredentialDto.md
|
||||||
doc/LoginResponseDto.md
|
doc/LoginResponseDto.md
|
||||||
doc/LogoutResponseDto.md
|
doc/LogoutResponseDto.md
|
||||||
|
doc/OAuthApi.md
|
||||||
|
doc/OAuthCallbackDto.md
|
||||||
|
doc/OAuthConfigDto.md
|
||||||
|
doc/OAuthConfigResponseDto.md
|
||||||
doc/RemoveAssetsDto.md
|
doc/RemoveAssetsDto.md
|
||||||
doc/SearchAssetDto.md
|
doc/SearchAssetDto.md
|
||||||
doc/ServerInfoApi.md
|
doc/ServerInfoApi.md
|
||||||
@@ -55,6 +59,10 @@ doc/ServerStatsResponseDto.md
|
|||||||
doc/ServerVersionReponseDto.md
|
doc/ServerVersionReponseDto.md
|
||||||
doc/SignUpDto.md
|
doc/SignUpDto.md
|
||||||
doc/SmartInfoResponseDto.md
|
doc/SmartInfoResponseDto.md
|
||||||
|
doc/SystemConfigApi.md
|
||||||
|
doc/SystemConfigKey.md
|
||||||
|
doc/SystemConfigResponseDto.md
|
||||||
|
doc/SystemConfigResponseItem.md
|
||||||
doc/ThumbnailFormat.md
|
doc/ThumbnailFormat.md
|
||||||
doc/TimeGroupEnum.md
|
doc/TimeGroupEnum.md
|
||||||
doc/UpdateAlbumDto.md
|
doc/UpdateAlbumDto.md
|
||||||
@@ -73,7 +81,9 @@ lib/api/asset_api.dart
|
|||||||
lib/api/authentication_api.dart
|
lib/api/authentication_api.dart
|
||||||
lib/api/device_info_api.dart
|
lib/api/device_info_api.dart
|
||||||
lib/api/job_api.dart
|
lib/api/job_api.dart
|
||||||
|
lib/api/o_auth_api.dart
|
||||||
lib/api/server_info_api.dart
|
lib/api/server_info_api.dart
|
||||||
|
lib/api/system_config_api.dart
|
||||||
lib/api/user_api.dart
|
lib/api/user_api.dart
|
||||||
lib/api_client.dart
|
lib/api_client.dart
|
||||||
lib/api_exception.dart
|
lib/api_exception.dart
|
||||||
@@ -122,6 +132,9 @@ lib/model/job_status_response_dto.dart
|
|||||||
lib/model/login_credential_dto.dart
|
lib/model/login_credential_dto.dart
|
||||||
lib/model/login_response_dto.dart
|
lib/model/login_response_dto.dart
|
||||||
lib/model/logout_response_dto.dart
|
lib/model/logout_response_dto.dart
|
||||||
|
lib/model/o_auth_callback_dto.dart
|
||||||
|
lib/model/o_auth_config_dto.dart
|
||||||
|
lib/model/o_auth_config_response_dto.dart
|
||||||
lib/model/remove_assets_dto.dart
|
lib/model/remove_assets_dto.dart
|
||||||
lib/model/search_asset_dto.dart
|
lib/model/search_asset_dto.dart
|
||||||
lib/model/server_info_response_dto.dart
|
lib/model/server_info_response_dto.dart
|
||||||
@@ -130,6 +143,9 @@ lib/model/server_stats_response_dto.dart
|
|||||||
lib/model/server_version_reponse_dto.dart
|
lib/model/server_version_reponse_dto.dart
|
||||||
lib/model/sign_up_dto.dart
|
lib/model/sign_up_dto.dart
|
||||||
lib/model/smart_info_response_dto.dart
|
lib/model/smart_info_response_dto.dart
|
||||||
|
lib/model/system_config_key.dart
|
||||||
|
lib/model/system_config_response_dto.dart
|
||||||
|
lib/model/system_config_response_item.dart
|
||||||
lib/model/thumbnail_format.dart
|
lib/model/thumbnail_format.dart
|
||||||
lib/model/time_group_enum.dart
|
lib/model/time_group_enum.dart
|
||||||
lib/model/update_album_dto.dart
|
lib/model/update_album_dto.dart
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ Class | Method | HTTP request | Description
|
|||||||
*AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
*AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
||||||
*AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
*AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
||||||
*AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset |
|
*AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset |
|
||||||
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download |
|
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download/{assetId} |
|
||||||
|
*AssetApi* | [**downloadLibrary**](doc//AssetApi.md#downloadlibrary) | **GET** /asset/download-library |
|
||||||
*AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |
|
*AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |
|
||||||
*AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{assetId} |
|
*AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{assetId} |
|
||||||
*AssetApi* | [**getAssetByTimeBucket**](doc//AssetApi.md#getassetbytimebucket) | **POST** /asset/time-bucket |
|
*AssetApi* | [**getAssetByTimeBucket**](doc//AssetApi.md#getassetbytimebucket) | **POST** /asset/time-bucket |
|
||||||
@@ -91,7 +92,7 @@ Class | Method | HTTP request | Description
|
|||||||
*AssetApi* | [**getCuratedObjects**](doc//AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects |
|
*AssetApi* | [**getCuratedObjects**](doc//AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects |
|
||||||
*AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
*AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
||||||
*AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search |
|
*AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search |
|
||||||
*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file |
|
*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{assetId} |
|
||||||
*AssetApi* | [**updateAssetById**](doc//AssetApi.md#updateassetbyid) | **PUT** /asset/assetById/{assetId} |
|
*AssetApi* | [**updateAssetById**](doc//AssetApi.md#updateassetbyid) | **PUT** /asset/assetById/{assetId} |
|
||||||
*AssetApi* | [**uploadFile**](doc//AssetApi.md#uploadfile) | **POST** /asset/upload |
|
*AssetApi* | [**uploadFile**](doc//AssetApi.md#uploadfile) | **POST** /asset/upload |
|
||||||
*AuthenticationApi* | [**adminSignUp**](doc//AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up |
|
*AuthenticationApi* | [**adminSignUp**](doc//AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up |
|
||||||
@@ -103,10 +104,14 @@ Class | Method | HTTP request | Description
|
|||||||
*JobApi* | [**getAllJobsStatus**](doc//JobApi.md#getalljobsstatus) | **GET** /jobs |
|
*JobApi* | [**getAllJobsStatus**](doc//JobApi.md#getalljobsstatus) | **GET** /jobs |
|
||||||
*JobApi* | [**getJobStatus**](doc//JobApi.md#getjobstatus) | **GET** /jobs/{jobId} |
|
*JobApi* | [**getJobStatus**](doc//JobApi.md#getjobstatus) | **GET** /jobs/{jobId} |
|
||||||
*JobApi* | [**sendJobCommand**](doc//JobApi.md#sendjobcommand) | **PUT** /jobs/{jobId} |
|
*JobApi* | [**sendJobCommand**](doc//JobApi.md#sendjobcommand) | **PUT** /jobs/{jobId} |
|
||||||
|
*OAuthApi* | [**callback**](doc//OAuthApi.md#callback) | **POST** /oauth/callback |
|
||||||
|
*OAuthApi* | [**generateConfig**](doc//OAuthApi.md#generateconfig) | **POST** /oauth/config |
|
||||||
*ServerInfoApi* | [**getServerInfo**](doc//ServerInfoApi.md#getserverinfo) | **GET** /server-info |
|
*ServerInfoApi* | [**getServerInfo**](doc//ServerInfoApi.md#getserverinfo) | **GET** /server-info |
|
||||||
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
|
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
|
||||||
*ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats |
|
*ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats |
|
||||||
*ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
|
*ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
|
||||||
|
*SystemConfigApi* | [**getConfig**](doc//SystemConfigApi.md#getconfig) | **GET** /system-config |
|
||||||
|
*SystemConfigApi* | [**updateConfig**](doc//SystemConfigApi.md#updateconfig) | **PUT** /system-config |
|
||||||
*UserApi* | [**createProfileImage**](doc//UserApi.md#createprofileimage) | **POST** /user/profile-image |
|
*UserApi* | [**createProfileImage**](doc//UserApi.md#createprofileimage) | **POST** /user/profile-image |
|
||||||
*UserApi* | [**createUser**](doc//UserApi.md#createuser) | **POST** /user |
|
*UserApi* | [**createUser**](doc//UserApi.md#createuser) | **POST** /user |
|
||||||
*UserApi* | [**deleteUser**](doc//UserApi.md#deleteuser) | **DELETE** /user/{userId} |
|
*UserApi* | [**deleteUser**](doc//UserApi.md#deleteuser) | **DELETE** /user/{userId} |
|
||||||
@@ -160,6 +165,9 @@ Class | Method | HTTP request | Description
|
|||||||
- [LoginCredentialDto](doc//LoginCredentialDto.md)
|
- [LoginCredentialDto](doc//LoginCredentialDto.md)
|
||||||
- [LoginResponseDto](doc//LoginResponseDto.md)
|
- [LoginResponseDto](doc//LoginResponseDto.md)
|
||||||
- [LogoutResponseDto](doc//LogoutResponseDto.md)
|
- [LogoutResponseDto](doc//LogoutResponseDto.md)
|
||||||
|
- [OAuthCallbackDto](doc//OAuthCallbackDto.md)
|
||||||
|
- [OAuthConfigDto](doc//OAuthConfigDto.md)
|
||||||
|
- [OAuthConfigResponseDto](doc//OAuthConfigResponseDto.md)
|
||||||
- [RemoveAssetsDto](doc//RemoveAssetsDto.md)
|
- [RemoveAssetsDto](doc//RemoveAssetsDto.md)
|
||||||
- [SearchAssetDto](doc//SearchAssetDto.md)
|
- [SearchAssetDto](doc//SearchAssetDto.md)
|
||||||
- [ServerInfoResponseDto](doc//ServerInfoResponseDto.md)
|
- [ServerInfoResponseDto](doc//ServerInfoResponseDto.md)
|
||||||
@@ -168,6 +176,9 @@ Class | Method | HTTP request | Description
|
|||||||
- [ServerVersionReponseDto](doc//ServerVersionReponseDto.md)
|
- [ServerVersionReponseDto](doc//ServerVersionReponseDto.md)
|
||||||
- [SignUpDto](doc//SignUpDto.md)
|
- [SignUpDto](doc//SignUpDto.md)
|
||||||
- [SmartInfoResponseDto](doc//SmartInfoResponseDto.md)
|
- [SmartInfoResponseDto](doc//SmartInfoResponseDto.md)
|
||||||
|
- [SystemConfigKey](doc//SystemConfigKey.md)
|
||||||
|
- [SystemConfigResponseDto](doc//SystemConfigResponseDto.md)
|
||||||
|
- [SystemConfigResponseItem](doc//SystemConfigResponseItem.md)
|
||||||
- [ThumbnailFormat](doc//ThumbnailFormat.md)
|
- [ThumbnailFormat](doc//ThumbnailFormat.md)
|
||||||
- [TimeGroupEnum](doc//TimeGroupEnum.md)
|
- [TimeGroupEnum](doc//TimeGroupEnum.md)
|
||||||
- [UpdateAlbumDto](doc//UpdateAlbumDto.md)
|
- [UpdateAlbumDto](doc//UpdateAlbumDto.md)
|
||||||
|
|||||||
15
mobile/openapi/doc/AdminConfigResponseDto.md
Normal file
15
mobile/openapi/doc/AdminConfigResponseDto.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# openapi.model.AdminConfigResponseDto
|
||||||
|
|
||||||
|
## Load the model package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
**config** | [**Object**](.md) | |
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
||||||
@@ -214,7 +214,7 @@ void (empty response body)
|
|||||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
# **downloadArchive**
|
# **downloadArchive**
|
||||||
> Object downloadArchive(albumId)
|
> Object downloadArchive(albumId, skip)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -230,9 +230,10 @@ import 'package:openapi/api.dart';
|
|||||||
|
|
||||||
final api_instance = AlbumApi();
|
final api_instance = AlbumApi();
|
||||||
final albumId = albumId_example; // String |
|
final albumId = albumId_example; // String |
|
||||||
|
final skip = 8.14; // num |
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final result = api_instance.downloadArchive(albumId);
|
final result = api_instance.downloadArchive(albumId, skip);
|
||||||
print(result);
|
print(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Exception when calling AlbumApi->downloadArchive: $e\n');
|
print('Exception when calling AlbumApi->downloadArchive: $e\n');
|
||||||
@@ -244,6 +245,7 @@ try {
|
|||||||
Name | Type | Description | Notes
|
Name | Type | Description | Notes
|
||||||
------------- | ------------- | ------------- | -------------
|
------------- | ------------- | ------------- | -------------
|
||||||
**albumId** | **String**| |
|
**albumId** | **String**| |
|
||||||
|
**skip** | **num**| | [optional]
|
||||||
|
|
||||||
### Return type
|
### Return type
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ Method | HTTP request | Description
|
|||||||
[**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
[**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
||||||
[**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
[**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
||||||
[**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset |
|
[**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset |
|
||||||
[**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download |
|
[**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download/{assetId} |
|
||||||
|
[**downloadLibrary**](AssetApi.md#downloadlibrary) | **GET** /asset/download-library |
|
||||||
[**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |
|
[**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |
|
||||||
[**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{assetId} |
|
[**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{assetId} |
|
||||||
[**getAssetByTimeBucket**](AssetApi.md#getassetbytimebucket) | **POST** /asset/time-bucket |
|
[**getAssetByTimeBucket**](AssetApi.md#getassetbytimebucket) | **POST** /asset/time-bucket |
|
||||||
@@ -24,7 +25,7 @@ Method | HTTP request | Description
|
|||||||
[**getCuratedObjects**](AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects |
|
[**getCuratedObjects**](AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects |
|
||||||
[**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
[**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
||||||
[**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search |
|
[**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search |
|
||||||
[**serveFile**](AssetApi.md#servefile) | **GET** /asset/file |
|
[**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{assetId} |
|
||||||
[**updateAssetById**](AssetApi.md#updateassetbyid) | **PUT** /asset/assetById/{assetId} |
|
[**updateAssetById**](AssetApi.md#updateassetbyid) | **PUT** /asset/assetById/{assetId} |
|
||||||
[**uploadFile**](AssetApi.md#uploadfile) | **POST** /asset/upload |
|
[**uploadFile**](AssetApi.md#uploadfile) | **POST** /asset/upload |
|
||||||
|
|
||||||
@@ -175,7 +176,7 @@ Name | Type | Description | Notes
|
|||||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
# **downloadFile**
|
# **downloadFile**
|
||||||
> Object downloadFile(aid, did, isThumb, isWeb)
|
> Object downloadFile(assetId, isThumb, isWeb)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -190,13 +191,12 @@ import 'package:openapi/api.dart';
|
|||||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||||
|
|
||||||
final api_instance = AssetApi();
|
final api_instance = AssetApi();
|
||||||
final aid = aid_example; // String |
|
final assetId = assetId_example; // String |
|
||||||
final did = did_example; // String |
|
|
||||||
final isThumb = true; // bool |
|
final isThumb = true; // bool |
|
||||||
final isWeb = true; // bool |
|
final isWeb = true; // bool |
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final result = api_instance.downloadFile(aid, did, isThumb, isWeb);
|
final result = api_instance.downloadFile(assetId, isThumb, isWeb);
|
||||||
print(result);
|
print(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Exception when calling AssetApi->downloadFile: $e\n');
|
print('Exception when calling AssetApi->downloadFile: $e\n');
|
||||||
@@ -207,8 +207,7 @@ try {
|
|||||||
|
|
||||||
Name | Type | Description | Notes
|
Name | Type | Description | Notes
|
||||||
------------- | ------------- | ------------- | -------------
|
------------- | ------------- | ------------- | -------------
|
||||||
**aid** | **String**| |
|
**assetId** | **String**| |
|
||||||
**did** | **String**| |
|
|
||||||
**isThumb** | **bool**| | [optional]
|
**isThumb** | **bool**| | [optional]
|
||||||
**isWeb** | **bool**| | [optional]
|
**isWeb** | **bool**| | [optional]
|
||||||
|
|
||||||
@@ -227,6 +226,53 @@ Name | Type | Description | Notes
|
|||||||
|
|
||||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
# **downloadLibrary**
|
||||||
|
> Object downloadLibrary(skip)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
// TODO Configure HTTP Bearer authorization: bearer
|
||||||
|
// Case 1. Use String Token
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||||
|
// Case 2. Use Function which generate token.
|
||||||
|
// String yourTokenGeneratorFunction() { ... }
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||||
|
|
||||||
|
final api_instance = AssetApi();
|
||||||
|
final skip = 8.14; // num |
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = api_instance.downloadLibrary(skip);
|
||||||
|
print(result);
|
||||||
|
} catch (e) {
|
||||||
|
print('Exception when calling AssetApi->downloadLibrary: $e\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------- | ------------- | ------------- | -------------
|
||||||
|
**skip** | **num**| | [optional]
|
||||||
|
|
||||||
|
### Return type
|
||||||
|
|
||||||
|
[**Object**](Object.md)
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
[bearer](../README.md#bearer)
|
||||||
|
|
||||||
|
### HTTP request headers
|
||||||
|
|
||||||
|
- **Content-Type**: Not defined
|
||||||
|
- **Accept**: application/json
|
||||||
|
|
||||||
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
# **getAllAssets**
|
# **getAllAssets**
|
||||||
> List<AssetResponseDto> getAllAssets()
|
> List<AssetResponseDto> getAllAssets()
|
||||||
|
|
||||||
@@ -733,7 +779,7 @@ Name | Type | Description | Notes
|
|||||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
# **serveFile**
|
# **serveFile**
|
||||||
> Object serveFile(aid, did, isThumb, isWeb)
|
> Object serveFile(assetId, isThumb, isWeb)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -748,13 +794,12 @@ import 'package:openapi/api.dart';
|
|||||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||||
|
|
||||||
final api_instance = AssetApi();
|
final api_instance = AssetApi();
|
||||||
final aid = aid_example; // String |
|
final assetId = assetId_example; // String |
|
||||||
final did = did_example; // String |
|
|
||||||
final isThumb = true; // bool |
|
final isThumb = true; // bool |
|
||||||
final isWeb = true; // bool |
|
final isWeb = true; // bool |
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final result = api_instance.serveFile(aid, did, isThumb, isWeb);
|
final result = api_instance.serveFile(assetId, isThumb, isWeb);
|
||||||
print(result);
|
print(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Exception when calling AssetApi->serveFile: $e\n');
|
print('Exception when calling AssetApi->serveFile: $e\n');
|
||||||
@@ -765,8 +810,7 @@ try {
|
|||||||
|
|
||||||
Name | Type | Description | Notes
|
Name | Type | Description | Notes
|
||||||
------------- | ------------- | ------------- | -------------
|
------------- | ------------- | ------------- | -------------
|
||||||
**aid** | **String**| |
|
**assetId** | **String**| |
|
||||||
**did** | **String**| |
|
|
||||||
**isThumb** | **bool**| | [optional]
|
**isThumb** | **bool**| | [optional]
|
||||||
**isWeb** | **bool**| | [optional]
|
**isWeb** | **bool**| | [optional]
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ import 'package:openapi/api.dart';
|
|||||||
## Properties
|
## Properties
|
||||||
Name | Type | Description | Notes
|
Name | Type | Description | Notes
|
||||||
------------ | ------------- | ------------- | -------------
|
------------ | ------------- | ------------- | -------------
|
||||||
**photos** | **int** | |
|
**audio** | **int** | | [default to 0]
|
||||||
**videos** | **int** | |
|
**photos** | **int** | | [default to 0]
|
||||||
|
**videos** | **int** | | [default to 0]
|
||||||
|
**other** | **int** | | [default to 0]
|
||||||
|
**total** | **int** | | [default to 0]
|
||||||
|
|
||||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ Name | Type | Description | Notes
|
|||||||
**encodedVideoPath** | **String** | |
|
**encodedVideoPath** | **String** | |
|
||||||
**exifInfo** | [**ExifResponseDto**](ExifResponseDto.md) | | [optional]
|
**exifInfo** | [**ExifResponseDto**](ExifResponseDto.md) | | [optional]
|
||||||
**smartInfo** | [**SmartInfoResponseDto**](SmartInfoResponseDto.md) | | [optional]
|
**smartInfo** | [**SmartInfoResponseDto**](SmartInfoResponseDto.md) | | [optional]
|
||||||
|
**livePhotoVideoId** | **String** | |
|
||||||
|
|
||||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|||||||
105
mobile/openapi/doc/ConfigApi.md
Normal file
105
mobile/openapi/doc/ConfigApi.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# openapi.api.ConfigApi
|
||||||
|
|
||||||
|
## Load the API package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
All URIs are relative to */api*
|
||||||
|
|
||||||
|
Method | HTTP request | Description
|
||||||
|
------------- | ------------- | -------------
|
||||||
|
[**getSystemConfig**](ConfigApi.md#getsystemconfig) | **GET** /config/system |
|
||||||
|
[**updateSystemConfig**](ConfigApi.md#updatesystemconfig) | **PUT** /config/system |
|
||||||
|
|
||||||
|
|
||||||
|
# **getSystemConfig**
|
||||||
|
> SystemConfigResponseDto getSystemConfig()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
// TODO Configure HTTP Bearer authorization: bearer
|
||||||
|
// Case 1. Use String Token
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||||
|
// Case 2. Use Function which generate token.
|
||||||
|
// String yourTokenGeneratorFunction() { ... }
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||||
|
|
||||||
|
final api_instance = ConfigApi();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = api_instance.getSystemConfig();
|
||||||
|
print(result);
|
||||||
|
} catch (e) {
|
||||||
|
print('Exception when calling ConfigApi->getSystemConfig: $e\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
This endpoint does not need any parameter.
|
||||||
|
|
||||||
|
### Return type
|
||||||
|
|
||||||
|
[**SystemConfigResponseDto**](SystemConfigResponseDto.md)
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
[bearer](../README.md#bearer)
|
||||||
|
|
||||||
|
### HTTP request headers
|
||||||
|
|
||||||
|
- **Content-Type**: Not defined
|
||||||
|
- **Accept**: application/json
|
||||||
|
|
||||||
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
# **updateSystemConfig**
|
||||||
|
> SystemConfigResponseDto updateSystemConfig(body)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
// TODO Configure HTTP Bearer authorization: bearer
|
||||||
|
// Case 1. Use String Token
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||||
|
// Case 2. Use Function which generate token.
|
||||||
|
// String yourTokenGeneratorFunction() { ... }
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||||
|
|
||||||
|
final api_instance = ConfigApi();
|
||||||
|
final body = Object(); // Object |
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = api_instance.updateSystemConfig(body);
|
||||||
|
print(result);
|
||||||
|
} catch (e) {
|
||||||
|
print('Exception when calling ConfigApi->updateSystemConfig: $e\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------- | ------------- | ------------- | -------------
|
||||||
|
**body** | **Object**| |
|
||||||
|
|
||||||
|
### Return type
|
||||||
|
|
||||||
|
[**SystemConfigResponseDto**](SystemConfigResponseDto.md)
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
[bearer](../README.md#bearer)
|
||||||
|
|
||||||
|
### HTTP request headers
|
||||||
|
|
||||||
|
- **Content-Type**: application/json
|
||||||
|
- **Accept**: application/json
|
||||||
|
|
||||||
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ import 'package:openapi/api.dart';
|
|||||||
Name | Type | Description | Notes
|
Name | Type | Description | Notes
|
||||||
------------ | ------------- | ------------- | -------------
|
------------ | ------------- | ------------- | -------------
|
||||||
**successful** | **bool** | | [readonly]
|
**successful** | **bool** | | [readonly]
|
||||||
|
**redirectUri** | **String** | | [readonly]
|
||||||
|
|
||||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|||||||
97
mobile/openapi/doc/OAuthApi.md
Normal file
97
mobile/openapi/doc/OAuthApi.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# openapi.api.OAuthApi
|
||||||
|
|
||||||
|
## Load the API package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
All URIs are relative to */api*
|
||||||
|
|
||||||
|
Method | HTTP request | Description
|
||||||
|
------------- | ------------- | -------------
|
||||||
|
[**callback**](OAuthApi.md#callback) | **POST** /oauth/callback |
|
||||||
|
[**generateConfig**](OAuthApi.md#generateconfig) | **POST** /oauth/config |
|
||||||
|
|
||||||
|
|
||||||
|
# **callback**
|
||||||
|
> LoginResponseDto callback(oAuthCallbackDto)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
|
final api_instance = OAuthApi();
|
||||||
|
final oAuthCallbackDto = OAuthCallbackDto(); // OAuthCallbackDto |
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = api_instance.callback(oAuthCallbackDto);
|
||||||
|
print(result);
|
||||||
|
} catch (e) {
|
||||||
|
print('Exception when calling OAuthApi->callback: $e\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------- | ------------- | ------------- | -------------
|
||||||
|
**oAuthCallbackDto** | [**OAuthCallbackDto**](OAuthCallbackDto.md)| |
|
||||||
|
|
||||||
|
### Return type
|
||||||
|
|
||||||
|
[**LoginResponseDto**](LoginResponseDto.md)
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
No authorization required
|
||||||
|
|
||||||
|
### HTTP request headers
|
||||||
|
|
||||||
|
- **Content-Type**: application/json
|
||||||
|
- **Accept**: application/json
|
||||||
|
|
||||||
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
# **generateConfig**
|
||||||
|
> OAuthConfigResponseDto generateConfig(oAuthConfigDto)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
|
final api_instance = OAuthApi();
|
||||||
|
final oAuthConfigDto = OAuthConfigDto(); // OAuthConfigDto |
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = api_instance.generateConfig(oAuthConfigDto);
|
||||||
|
print(result);
|
||||||
|
} catch (e) {
|
||||||
|
print('Exception when calling OAuthApi->generateConfig: $e\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------- | ------------- | ------------- | -------------
|
||||||
|
**oAuthConfigDto** | [**OAuthConfigDto**](OAuthConfigDto.md)| |
|
||||||
|
|
||||||
|
### Return type
|
||||||
|
|
||||||
|
[**OAuthConfigResponseDto**](OAuthConfigResponseDto.md)
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
No authorization required
|
||||||
|
|
||||||
|
### HTTP request headers
|
||||||
|
|
||||||
|
- **Content-Type**: application/json
|
||||||
|
- **Accept**: application/json
|
||||||
|
|
||||||
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
15
mobile/openapi/doc/OAuthCallbackDto.md
Normal file
15
mobile/openapi/doc/OAuthCallbackDto.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# openapi.model.OAuthCallbackDto
|
||||||
|
|
||||||
|
## Load the model package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
**url** | **String** | |
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
||||||
15
mobile/openapi/doc/OAuthConfigDto.md
Normal file
15
mobile/openapi/doc/OAuthConfigDto.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# openapi.model.OAuthConfigDto
|
||||||
|
|
||||||
|
## Load the model package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
**redirectUri** | **String** | |
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
||||||
17
mobile/openapi/doc/OAuthConfigResponseDto.md
Normal file
17
mobile/openapi/doc/OAuthConfigResponseDto.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# openapi.model.OAuthConfigResponseDto
|
||||||
|
|
||||||
|
## Load the model package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
**enabled** | **bool** | | [readonly]
|
||||||
|
**url** | **String** | | [optional] [readonly]
|
||||||
|
**buttonText** | **String** | | [optional] [readonly]
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
||||||
105
mobile/openapi/doc/SystemConfigApi.md
Normal file
105
mobile/openapi/doc/SystemConfigApi.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# openapi.api.SystemConfigApi
|
||||||
|
|
||||||
|
## Load the API package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
All URIs are relative to */api*
|
||||||
|
|
||||||
|
Method | HTTP request | Description
|
||||||
|
------------- | ------------- | -------------
|
||||||
|
[**getConfig**](SystemConfigApi.md#getconfig) | **GET** /system-config |
|
||||||
|
[**updateConfig**](SystemConfigApi.md#updateconfig) | **PUT** /system-config |
|
||||||
|
|
||||||
|
|
||||||
|
# **getConfig**
|
||||||
|
> SystemConfigResponseDto getConfig()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
// TODO Configure HTTP Bearer authorization: bearer
|
||||||
|
// Case 1. Use String Token
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||||
|
// Case 2. Use Function which generate token.
|
||||||
|
// String yourTokenGeneratorFunction() { ... }
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||||
|
|
||||||
|
final api_instance = SystemConfigApi();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = api_instance.getConfig();
|
||||||
|
print(result);
|
||||||
|
} catch (e) {
|
||||||
|
print('Exception when calling SystemConfigApi->getConfig: $e\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
This endpoint does not need any parameter.
|
||||||
|
|
||||||
|
### Return type
|
||||||
|
|
||||||
|
[**SystemConfigResponseDto**](SystemConfigResponseDto.md)
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
[bearer](../README.md#bearer)
|
||||||
|
|
||||||
|
### HTTP request headers
|
||||||
|
|
||||||
|
- **Content-Type**: Not defined
|
||||||
|
- **Accept**: application/json
|
||||||
|
|
||||||
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
# **updateConfig**
|
||||||
|
> SystemConfigResponseDto updateConfig(body)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
// TODO Configure HTTP Bearer authorization: bearer
|
||||||
|
// Case 1. Use String Token
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||||
|
// Case 2. Use Function which generate token.
|
||||||
|
// String yourTokenGeneratorFunction() { ... }
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||||
|
|
||||||
|
final api_instance = SystemConfigApi();
|
||||||
|
final body = Object(); // Object |
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = api_instance.updateConfig(body);
|
||||||
|
print(result);
|
||||||
|
} catch (e) {
|
||||||
|
print('Exception when calling SystemConfigApi->updateConfig: $e\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------- | ------------- | ------------- | -------------
|
||||||
|
**body** | **Object**| |
|
||||||
|
|
||||||
|
### Return type
|
||||||
|
|
||||||
|
[**SystemConfigResponseDto**](SystemConfigResponseDto.md)
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
[bearer](../README.md#bearer)
|
||||||
|
|
||||||
|
### HTTP request headers
|
||||||
|
|
||||||
|
- **Content-Type**: application/json
|
||||||
|
- **Accept**: application/json
|
||||||
|
|
||||||
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
16
mobile/openapi/doc/SystemConfigEntity.md
Normal file
16
mobile/openapi/doc/SystemConfigEntity.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# openapi.model.SystemConfigEntity
|
||||||
|
|
||||||
|
## Load the model package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
**key** | **String** | |
|
||||||
|
**value** | [**Object**](.md) | |
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
||||||
14
mobile/openapi/doc/SystemConfigKey.md
Normal file
14
mobile/openapi/doc/SystemConfigKey.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# openapi.model.SystemConfigKey
|
||||||
|
|
||||||
|
## Load the model package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
||||||
15
mobile/openapi/doc/SystemConfigResponseDto.md
Normal file
15
mobile/openapi/doc/SystemConfigResponseDto.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# openapi.model.SystemConfigResponseDto
|
||||||
|
|
||||||
|
## Load the model package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
**config** | [**List<SystemConfigResponseItem>**](SystemConfigResponseItem.md) | | [default to const []]
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
||||||
18
mobile/openapi/doc/SystemConfigResponseItem.md
Normal file
18
mobile/openapi/doc/SystemConfigResponseItem.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# openapi.model.SystemConfigResponseItem
|
||||||
|
|
||||||
|
## Load the model package
|
||||||
|
```dart
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
**name** | **String** | |
|
||||||
|
**key** | [**SystemConfigKey**](SystemConfigKey.md) | |
|
||||||
|
**value** | **String** | |
|
||||||
|
**defaultValue** | **String** | |
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
||||||
@@ -9,7 +9,6 @@ import 'package:openapi/api.dart';
|
|||||||
Name | Type | Description | Notes
|
Name | Type | Description | Notes
|
||||||
------------ | ------------- | ------------- | -------------
|
------------ | ------------- | ------------- | -------------
|
||||||
**userId** | **String** | |
|
**userId** | **String** | |
|
||||||
**objects** | **int** | |
|
|
||||||
**videos** | **int** | |
|
**videos** | **int** | |
|
||||||
**photos** | **int** | |
|
**photos** | **int** | |
|
||||||
**usageRaw** | **int** | |
|
**usageRaw** | **int** | |
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ part 'api/asset_api.dart';
|
|||||||
part 'api/authentication_api.dart';
|
part 'api/authentication_api.dart';
|
||||||
part 'api/device_info_api.dart';
|
part 'api/device_info_api.dart';
|
||||||
part 'api/job_api.dart';
|
part 'api/job_api.dart';
|
||||||
|
part 'api/o_auth_api.dart';
|
||||||
part 'api/server_info_api.dart';
|
part 'api/server_info_api.dart';
|
||||||
|
part 'api/system_config_api.dart';
|
||||||
part 'api/user_api.dart';
|
part 'api/user_api.dart';
|
||||||
|
|
||||||
part 'model/add_assets_dto.dart';
|
part 'model/add_assets_dto.dart';
|
||||||
@@ -74,6 +76,9 @@ part 'model/job_status_response_dto.dart';
|
|||||||
part 'model/login_credential_dto.dart';
|
part 'model/login_credential_dto.dart';
|
||||||
part 'model/login_response_dto.dart';
|
part 'model/login_response_dto.dart';
|
||||||
part 'model/logout_response_dto.dart';
|
part 'model/logout_response_dto.dart';
|
||||||
|
part 'model/o_auth_callback_dto.dart';
|
||||||
|
part 'model/o_auth_config_dto.dart';
|
||||||
|
part 'model/o_auth_config_response_dto.dart';
|
||||||
part 'model/remove_assets_dto.dart';
|
part 'model/remove_assets_dto.dart';
|
||||||
part 'model/search_asset_dto.dart';
|
part 'model/search_asset_dto.dart';
|
||||||
part 'model/server_info_response_dto.dart';
|
part 'model/server_info_response_dto.dart';
|
||||||
@@ -82,6 +87,9 @@ part 'model/server_stats_response_dto.dart';
|
|||||||
part 'model/server_version_reponse_dto.dart';
|
part 'model/server_version_reponse_dto.dart';
|
||||||
part 'model/sign_up_dto.dart';
|
part 'model/sign_up_dto.dart';
|
||||||
part 'model/smart_info_response_dto.dart';
|
part 'model/smart_info_response_dto.dart';
|
||||||
|
part 'model/system_config_key.dart';
|
||||||
|
part 'model/system_config_response_dto.dart';
|
||||||
|
part 'model/system_config_response_item.dart';
|
||||||
part 'model/thumbnail_format.dart';
|
part 'model/thumbnail_format.dart';
|
||||||
part 'model/time_group_enum.dart';
|
part 'model/time_group_enum.dart';
|
||||||
part 'model/update_album_dto.dart';
|
part 'model/update_album_dto.dart';
|
||||||
|
|||||||
@@ -211,7 +211,9 @@ class AlbumApi {
|
|||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
/// * [String] albumId (required):
|
/// * [String] albumId (required):
|
||||||
Future<Response> downloadArchiveWithHttpInfo(String albumId,) async {
|
///
|
||||||
|
/// * [num] skip:
|
||||||
|
Future<Response> downloadArchiveWithHttpInfo(String albumId, { num? skip, }) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final path = r'/album/{albumId}/download'
|
final path = r'/album/{albumId}/download'
|
||||||
.replaceAll('{albumId}', albumId);
|
.replaceAll('{albumId}', albumId);
|
||||||
@@ -223,6 +225,10 @@ class AlbumApi {
|
|||||||
final headerParams = <String, String>{};
|
final headerParams = <String, String>{};
|
||||||
final formParams = <String, String>{};
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
if (skip != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'skip', skip));
|
||||||
|
}
|
||||||
|
|
||||||
const contentTypes = <String>[];
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
@@ -240,8 +246,10 @@ class AlbumApi {
|
|||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
/// * [String] albumId (required):
|
/// * [String] albumId (required):
|
||||||
Future<Object?> downloadArchive(String albumId,) async {
|
///
|
||||||
final response = await downloadArchiveWithHttpInfo(albumId,);
|
/// * [num] skip:
|
||||||
|
Future<Object?> downloadArchive(String albumId, { num? skip, }) async {
|
||||||
|
final response = await downloadArchiveWithHttpInfo(albumId, skip: skip, );
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,19 +178,18 @@ class AssetApi {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs an HTTP 'GET /asset/download' operation and returns the [Response].
|
/// Performs an HTTP 'GET /asset/download/{assetId}' operation and returns the [Response].
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
/// * [String] aid (required):
|
/// * [String] assetId (required):
|
||||||
///
|
|
||||||
/// * [String] did (required):
|
|
||||||
///
|
///
|
||||||
/// * [bool] isThumb:
|
/// * [bool] isThumb:
|
||||||
///
|
///
|
||||||
/// * [bool] isWeb:
|
/// * [bool] isWeb:
|
||||||
Future<Response> downloadFileWithHttpInfo(String aid, String did, { bool? isThumb, bool? isWeb, }) async {
|
Future<Response> downloadFileWithHttpInfo(String assetId, { bool? isThumb, bool? isWeb, }) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final path = r'/asset/download';
|
final path = r'/asset/download/{assetId}'
|
||||||
|
.replaceAll('{assetId}', assetId);
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
// ignore: prefer_final_locals
|
||||||
Object? postBody;
|
Object? postBody;
|
||||||
@@ -199,8 +198,6 @@ class AssetApi {
|
|||||||
final headerParams = <String, String>{};
|
final headerParams = <String, String>{};
|
||||||
final formParams = <String, String>{};
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
queryParams.addAll(_queryParams('', 'aid', aid));
|
|
||||||
queryParams.addAll(_queryParams('', 'did', did));
|
|
||||||
if (isThumb != null) {
|
if (isThumb != null) {
|
||||||
queryParams.addAll(_queryParams('', 'isThumb', isThumb));
|
queryParams.addAll(_queryParams('', 'isThumb', isThumb));
|
||||||
}
|
}
|
||||||
@@ -224,15 +221,64 @@ class AssetApi {
|
|||||||
|
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
/// * [String] aid (required):
|
/// * [String] assetId (required):
|
||||||
///
|
|
||||||
/// * [String] did (required):
|
|
||||||
///
|
///
|
||||||
/// * [bool] isThumb:
|
/// * [bool] isThumb:
|
||||||
///
|
///
|
||||||
/// * [bool] isWeb:
|
/// * [bool] isWeb:
|
||||||
Future<Object?> downloadFile(String aid, String did, { bool? isThumb, bool? isWeb, }) async {
|
Future<Object?> downloadFile(String assetId, { bool? isThumb, bool? isWeb, }) async {
|
||||||
final response = await downloadFileWithHttpInfo(aid, did, isThumb: isThumb, isWeb: isWeb, );
|
final response = await downloadFileWithHttpInfo(assetId, isThumb: isThumb, isWeb: isWeb, );
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'Object',) as Object;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /asset/download-library' operation and returns the [Response].
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [num] skip:
|
||||||
|
Future<Response> downloadLibraryWithHttpInfo({ num? skip, }) async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/asset/download-library';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
if (skip != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'skip', skip));
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
path,
|
||||||
|
'GET',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [num] skip:
|
||||||
|
Future<Object?> downloadLibrary({ num? skip, }) async {
|
||||||
|
final response = await downloadLibraryWithHttpInfo( skip: skip, );
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
}
|
}
|
||||||
@@ -790,19 +836,18 @@ class AssetApi {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs an HTTP 'GET /asset/file' operation and returns the [Response].
|
/// Performs an HTTP 'GET /asset/file/{assetId}' operation and returns the [Response].
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
/// * [String] aid (required):
|
/// * [String] assetId (required):
|
||||||
///
|
|
||||||
/// * [String] did (required):
|
|
||||||
///
|
///
|
||||||
/// * [bool] isThumb:
|
/// * [bool] isThumb:
|
||||||
///
|
///
|
||||||
/// * [bool] isWeb:
|
/// * [bool] isWeb:
|
||||||
Future<Response> serveFileWithHttpInfo(String aid, String did, { bool? isThumb, bool? isWeb, }) async {
|
Future<Response> serveFileWithHttpInfo(String assetId, { bool? isThumb, bool? isWeb, }) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final path = r'/asset/file';
|
final path = r'/asset/file/{assetId}'
|
||||||
|
.replaceAll('{assetId}', assetId);
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
// ignore: prefer_final_locals
|
||||||
Object? postBody;
|
Object? postBody;
|
||||||
@@ -811,8 +856,6 @@ class AssetApi {
|
|||||||
final headerParams = <String, String>{};
|
final headerParams = <String, String>{};
|
||||||
final formParams = <String, String>{};
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
queryParams.addAll(_queryParams('', 'aid', aid));
|
|
||||||
queryParams.addAll(_queryParams('', 'did', did));
|
|
||||||
if (isThumb != null) {
|
if (isThumb != null) {
|
||||||
queryParams.addAll(_queryParams('', 'isThumb', isThumb));
|
queryParams.addAll(_queryParams('', 'isThumb', isThumb));
|
||||||
}
|
}
|
||||||
@@ -836,15 +879,13 @@ class AssetApi {
|
|||||||
|
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
/// * [String] aid (required):
|
/// * [String] assetId (required):
|
||||||
///
|
|
||||||
/// * [String] did (required):
|
|
||||||
///
|
///
|
||||||
/// * [bool] isThumb:
|
/// * [bool] isThumb:
|
||||||
///
|
///
|
||||||
/// * [bool] isWeb:
|
/// * [bool] isWeb:
|
||||||
Future<Object?> serveFile(String aid, String did, { bool? isThumb, bool? isWeb, }) async {
|
Future<Object?> serveFile(String assetId, { bool? isThumb, bool? isWeb, }) async {
|
||||||
final response = await serveFileWithHttpInfo(aid, did, isThumb: isThumb, isWeb: isWeb, );
|
final response = await serveFileWithHttpInfo(assetId, isThumb: isThumb, isWeb: isWeb, );
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
}
|
}
|
||||||
|
|||||||
106
mobile/openapi/lib/api/config_api.dart
Normal file
106
mobile/openapi/lib/api/config_api.dart
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.12
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigApi {
|
||||||
|
ConfigApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;
|
||||||
|
|
||||||
|
final ApiClient apiClient;
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /config/system' operation and returns the [Response].
|
||||||
|
Future<Response> getSystemConfigWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/config/system';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
path,
|
||||||
|
'GET',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SystemConfigResponseDto?> getSystemConfig() async {
|
||||||
|
final response = await getSystemConfigWithHttpInfo();
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SystemConfigResponseDto',) as SystemConfigResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'PUT /config/system' operation and returns the [Response].
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [Object] body (required):
|
||||||
|
Future<Response> updateSystemConfigWithHttpInfo(Object body,) async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/config/system';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody = body;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>['application/json'];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
path,
|
||||||
|
'PUT',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [Object] body (required):
|
||||||
|
Future<SystemConfigResponseDto?> updateSystemConfig(Object body,) async {
|
||||||
|
final response = await updateSystemConfigWithHttpInfo(body,);
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SystemConfigResponseDto',) as SystemConfigResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
112
mobile/openapi/lib/api/o_auth_api.dart
Normal file
112
mobile/openapi/lib/api/o_auth_api.dart
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.12
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthApi {
|
||||||
|
OAuthApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;
|
||||||
|
|
||||||
|
final ApiClient apiClient;
|
||||||
|
|
||||||
|
/// Performs an HTTP 'POST /oauth/callback' operation and returns the [Response].
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [OAuthCallbackDto] oAuthCallbackDto (required):
|
||||||
|
Future<Response> callbackWithHttpInfo(OAuthCallbackDto oAuthCallbackDto,) async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/oauth/callback';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody = oAuthCallbackDto;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>['application/json'];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
path,
|
||||||
|
'POST',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [OAuthCallbackDto] oAuthCallbackDto (required):
|
||||||
|
Future<LoginResponseDto?> callback(OAuthCallbackDto oAuthCallbackDto,) async {
|
||||||
|
final response = await callbackWithHttpInfo(oAuthCallbackDto,);
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'LoginResponseDto',) as LoginResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'POST /oauth/config' operation and returns the [Response].
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [OAuthConfigDto] oAuthConfigDto (required):
|
||||||
|
Future<Response> generateConfigWithHttpInfo(OAuthConfigDto oAuthConfigDto,) async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/oauth/config';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody = oAuthConfigDto;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>['application/json'];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
path,
|
||||||
|
'POST',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [OAuthConfigDto] oAuthConfigDto (required):
|
||||||
|
Future<OAuthConfigResponseDto?> generateConfig(OAuthConfigDto oAuthConfigDto,) async {
|
||||||
|
final response = await generateConfigWithHttpInfo(oAuthConfigDto,);
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'OAuthConfigResponseDto',) as OAuthConfigResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
106
mobile/openapi/lib/api/system_config_api.dart
Normal file
106
mobile/openapi/lib/api/system_config_api.dart
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.12
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
|
||||||
|
class SystemConfigApi {
|
||||||
|
SystemConfigApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;
|
||||||
|
|
||||||
|
final ApiClient apiClient;
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /system-config' operation and returns the [Response].
|
||||||
|
Future<Response> getConfigWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/system-config';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
path,
|
||||||
|
'GET',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SystemConfigResponseDto?> getConfig() async {
|
||||||
|
final response = await getConfigWithHttpInfo();
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SystemConfigResponseDto',) as SystemConfigResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'PUT /system-config' operation and returns the [Response].
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [Object] body (required):
|
||||||
|
Future<Response> updateConfigWithHttpInfo(Object body,) async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/system-config';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody = body;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>['application/json'];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
path,
|
||||||
|
'PUT',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [Object] body (required):
|
||||||
|
Future<SystemConfigResponseDto?> updateConfig(Object body,) async {
|
||||||
|
final response = await updateConfigWithHttpInfo(body,);
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SystemConfigResponseDto',) as SystemConfigResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -270,6 +270,12 @@ class ApiClient {
|
|||||||
return LoginResponseDto.fromJson(value);
|
return LoginResponseDto.fromJson(value);
|
||||||
case 'LogoutResponseDto':
|
case 'LogoutResponseDto':
|
||||||
return LogoutResponseDto.fromJson(value);
|
return LogoutResponseDto.fromJson(value);
|
||||||
|
case 'OAuthCallbackDto':
|
||||||
|
return OAuthCallbackDto.fromJson(value);
|
||||||
|
case 'OAuthConfigDto':
|
||||||
|
return OAuthConfigDto.fromJson(value);
|
||||||
|
case 'OAuthConfigResponseDto':
|
||||||
|
return OAuthConfigResponseDto.fromJson(value);
|
||||||
case 'RemoveAssetsDto':
|
case 'RemoveAssetsDto':
|
||||||
return RemoveAssetsDto.fromJson(value);
|
return RemoveAssetsDto.fromJson(value);
|
||||||
case 'SearchAssetDto':
|
case 'SearchAssetDto':
|
||||||
@@ -286,6 +292,12 @@ class ApiClient {
|
|||||||
return SignUpDto.fromJson(value);
|
return SignUpDto.fromJson(value);
|
||||||
case 'SmartInfoResponseDto':
|
case 'SmartInfoResponseDto':
|
||||||
return SmartInfoResponseDto.fromJson(value);
|
return SmartInfoResponseDto.fromJson(value);
|
||||||
|
case 'SystemConfigKey':
|
||||||
|
return SystemConfigKeyTypeTransformer().decode(value);
|
||||||
|
case 'SystemConfigResponseDto':
|
||||||
|
return SystemConfigResponseDto.fromJson(value);
|
||||||
|
case 'SystemConfigResponseItem':
|
||||||
|
return SystemConfigResponseItem.fromJson(value);
|
||||||
case 'ThumbnailFormat':
|
case 'ThumbnailFormat':
|
||||||
return ThumbnailFormatTypeTransformer().decode(value);
|
return ThumbnailFormatTypeTransformer().decode(value);
|
||||||
case 'TimeGroupEnum':
|
case 'TimeGroupEnum':
|
||||||
|
|||||||
@@ -70,6 +70,9 @@ String parameterToString(dynamic value) {
|
|||||||
if (value is JobId) {
|
if (value is JobId) {
|
||||||
return JobIdTypeTransformer().encode(value).toString();
|
return JobIdTypeTransformer().encode(value).toString();
|
||||||
}
|
}
|
||||||
|
if (value is SystemConfigKey) {
|
||||||
|
return SystemConfigKeyTypeTransformer().encode(value).toString();
|
||||||
|
}
|
||||||
if (value is ThumbnailFormat) {
|
if (value is ThumbnailFormat) {
|
||||||
return ThumbnailFormatTypeTransformer().encode(value).toString();
|
return ThumbnailFormatTypeTransformer().encode(value).toString();
|
||||||
}
|
}
|
||||||
|
|||||||
111
mobile/openapi/lib/model/admin_config_response_dto.dart
Normal file
111
mobile/openapi/lib/model/admin_config_response_dto.dart
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.12
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class AdminConfigResponseDto {
|
||||||
|
/// Returns a new [AdminConfigResponseDto] instance.
|
||||||
|
AdminConfigResponseDto({
|
||||||
|
required this.config,
|
||||||
|
});
|
||||||
|
|
||||||
|
Object config;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is AdminConfigResponseDto &&
|
||||||
|
other.config == config;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(config.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'AdminConfigResponseDto[config=$config]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final _json = <String, dynamic>{};
|
||||||
|
_json[r'config'] = config;
|
||||||
|
return _json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [AdminConfigResponseDto] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static AdminConfigResponseDto? fromJson(dynamic value) {
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
// Ensure that the map contains the required keys.
|
||||||
|
// Note 1: the values aren't checked for validity beyond being non-null.
|
||||||
|
// Note 2: this code is stripped in release mode!
|
||||||
|
assert(() {
|
||||||
|
requiredKeys.forEach((key) {
|
||||||
|
assert(json.containsKey(key), 'Required key "AdminConfigResponseDto[$key]" is missing from JSON.');
|
||||||
|
assert(json[key] != null, 'Required key "AdminConfigResponseDto[$key]" has a null value in JSON.');
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
|
||||||
|
return AdminConfigResponseDto(
|
||||||
|
config: mapValueOfType<Object>(json, r'config')!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<AdminConfigResponseDto>? listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <AdminConfigResponseDto>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = AdminConfigResponseDto.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, AdminConfigResponseDto> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, AdminConfigResponseDto>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = AdminConfigResponseDto.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of AdminConfigResponseDto-objects as value to a dart map
|
||||||
|
static Map<String, List<AdminConfigResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<AdminConfigResponseDto>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = AdminConfigResponseDto.listFromJson(entry.value, growable: growable,);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'config',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@@ -13,32 +13,50 @@ part of openapi.api;
|
|||||||
class AssetCountByUserIdResponseDto {
|
class AssetCountByUserIdResponseDto {
|
||||||
/// Returns a new [AssetCountByUserIdResponseDto] instance.
|
/// Returns a new [AssetCountByUserIdResponseDto] instance.
|
||||||
AssetCountByUserIdResponseDto({
|
AssetCountByUserIdResponseDto({
|
||||||
required this.photos,
|
this.audio = 0,
|
||||||
required this.videos,
|
this.photos = 0,
|
||||||
|
this.videos = 0,
|
||||||
|
this.other = 0,
|
||||||
|
this.total = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
int audio;
|
||||||
|
|
||||||
int photos;
|
int photos;
|
||||||
|
|
||||||
int videos;
|
int videos;
|
||||||
|
|
||||||
|
int other;
|
||||||
|
|
||||||
|
int total;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is AssetCountByUserIdResponseDto &&
|
bool operator ==(Object other) => identical(this, other) || other is AssetCountByUserIdResponseDto &&
|
||||||
|
other.audio == audio &&
|
||||||
other.photos == photos &&
|
other.photos == photos &&
|
||||||
other.videos == videos;
|
other.videos == videos &&
|
||||||
|
other.other == other &&
|
||||||
|
other.total == total;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
// ignore: unnecessary_parenthesis
|
// ignore: unnecessary_parenthesis
|
||||||
|
(audio.hashCode) +
|
||||||
(photos.hashCode) +
|
(photos.hashCode) +
|
||||||
(videos.hashCode);
|
(videos.hashCode) +
|
||||||
|
(other.hashCode) +
|
||||||
|
(total.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'AssetCountByUserIdResponseDto[photos=$photos, videos=$videos]';
|
String toString() => 'AssetCountByUserIdResponseDto[audio=$audio, photos=$photos, videos=$videos, other=$other, total=$total]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final _json = <String, dynamic>{};
|
final _json = <String, dynamic>{};
|
||||||
|
_json[r'audio'] = audio;
|
||||||
_json[r'photos'] = photos;
|
_json[r'photos'] = photos;
|
||||||
_json[r'videos'] = videos;
|
_json[r'videos'] = videos;
|
||||||
|
_json[r'other'] = other;
|
||||||
|
_json[r'total'] = total;
|
||||||
return _json;
|
return _json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,8 +79,11 @@ class AssetCountByUserIdResponseDto {
|
|||||||
}());
|
}());
|
||||||
|
|
||||||
return AssetCountByUserIdResponseDto(
|
return AssetCountByUserIdResponseDto(
|
||||||
|
audio: mapValueOfType<int>(json, r'audio')!,
|
||||||
photos: mapValueOfType<int>(json, r'photos')!,
|
photos: mapValueOfType<int>(json, r'photos')!,
|
||||||
videos: mapValueOfType<int>(json, r'videos')!,
|
videos: mapValueOfType<int>(json, r'videos')!,
|
||||||
|
other: mapValueOfType<int>(json, r'other')!,
|
||||||
|
total: mapValueOfType<int>(json, r'total')!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -112,8 +133,11 @@ class AssetCountByUserIdResponseDto {
|
|||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
/// The list of required keys that must be present in a JSON.
|
||||||
static const requiredKeys = <String>{
|
static const requiredKeys = <String>{
|
||||||
|
'audio',
|
||||||
'photos',
|
'photos',
|
||||||
'videos',
|
'videos',
|
||||||
|
'other',
|
||||||
|
'total',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ class AssetResponseDto {
|
|||||||
required this.encodedVideoPath,
|
required this.encodedVideoPath,
|
||||||
this.exifInfo,
|
this.exifInfo,
|
||||||
this.smartInfo,
|
this.smartInfo,
|
||||||
|
required this.livePhotoVideoId,
|
||||||
});
|
});
|
||||||
|
|
||||||
AssetTypeEnum type;
|
AssetTypeEnum type;
|
||||||
@@ -75,6 +76,8 @@ class AssetResponseDto {
|
|||||||
///
|
///
|
||||||
SmartInfoResponseDto? smartInfo;
|
SmartInfoResponseDto? smartInfo;
|
||||||
|
|
||||||
|
String? livePhotoVideoId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto &&
|
bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto &&
|
||||||
other.type == type &&
|
other.type == type &&
|
||||||
@@ -92,7 +95,8 @@ class AssetResponseDto {
|
|||||||
other.webpPath == webpPath &&
|
other.webpPath == webpPath &&
|
||||||
other.encodedVideoPath == encodedVideoPath &&
|
other.encodedVideoPath == encodedVideoPath &&
|
||||||
other.exifInfo == exifInfo &&
|
other.exifInfo == exifInfo &&
|
||||||
other.smartInfo == smartInfo;
|
other.smartInfo == smartInfo &&
|
||||||
|
other.livePhotoVideoId == livePhotoVideoId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
@@ -112,10 +116,11 @@ class AssetResponseDto {
|
|||||||
(webpPath == null ? 0 : webpPath!.hashCode) +
|
(webpPath == null ? 0 : webpPath!.hashCode) +
|
||||||
(encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) +
|
(encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) +
|
||||||
(exifInfo == null ? 0 : exifInfo!.hashCode) +
|
(exifInfo == null ? 0 : exifInfo!.hashCode) +
|
||||||
(smartInfo == null ? 0 : smartInfo!.hashCode);
|
(smartInfo == null ? 0 : smartInfo!.hashCode) +
|
||||||
|
(livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo]';
|
String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final _json = <String, dynamic>{};
|
final _json = <String, dynamic>{};
|
||||||
@@ -159,6 +164,11 @@ class AssetResponseDto {
|
|||||||
} else {
|
} else {
|
||||||
_json[r'smartInfo'] = null;
|
_json[r'smartInfo'] = null;
|
||||||
}
|
}
|
||||||
|
if (livePhotoVideoId != null) {
|
||||||
|
_json[r'livePhotoVideoId'] = livePhotoVideoId;
|
||||||
|
} else {
|
||||||
|
_json[r'livePhotoVideoId'] = null;
|
||||||
|
}
|
||||||
return _json;
|
return _json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +207,7 @@ class AssetResponseDto {
|
|||||||
encodedVideoPath: mapValueOfType<String>(json, r'encodedVideoPath'),
|
encodedVideoPath: mapValueOfType<String>(json, r'encodedVideoPath'),
|
||||||
exifInfo: ExifResponseDto.fromJson(json[r'exifInfo']),
|
exifInfo: ExifResponseDto.fromJson(json[r'exifInfo']),
|
||||||
smartInfo: SmartInfoResponseDto.fromJson(json[r'smartInfo']),
|
smartInfo: SmartInfoResponseDto.fromJson(json[r'smartInfo']),
|
||||||
|
livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -260,6 +271,7 @@ class AssetResponseDto {
|
|||||||
'duration',
|
'duration',
|
||||||
'webpPath',
|
'webpPath',
|
||||||
'encodedVideoPath',
|
'encodedVideoPath',
|
||||||
|
'livePhotoVideoId',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,25 +14,31 @@ class LogoutResponseDto {
|
|||||||
/// Returns a new [LogoutResponseDto] instance.
|
/// Returns a new [LogoutResponseDto] instance.
|
||||||
LogoutResponseDto({
|
LogoutResponseDto({
|
||||||
required this.successful,
|
required this.successful,
|
||||||
|
required this.redirectUri,
|
||||||
});
|
});
|
||||||
|
|
||||||
bool successful;
|
bool successful;
|
||||||
|
|
||||||
|
String redirectUri;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is LogoutResponseDto &&
|
bool operator ==(Object other) => identical(this, other) || other is LogoutResponseDto &&
|
||||||
other.successful == successful;
|
other.successful == successful &&
|
||||||
|
other.redirectUri == redirectUri;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
// ignore: unnecessary_parenthesis
|
// ignore: unnecessary_parenthesis
|
||||||
(successful.hashCode);
|
(successful.hashCode) +
|
||||||
|
(redirectUri.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'LogoutResponseDto[successful=$successful]';
|
String toString() => 'LogoutResponseDto[successful=$successful, redirectUri=$redirectUri]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final _json = <String, dynamic>{};
|
final _json = <String, dynamic>{};
|
||||||
_json[r'successful'] = successful;
|
_json[r'successful'] = successful;
|
||||||
|
_json[r'redirectUri'] = redirectUri;
|
||||||
return _json;
|
return _json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,6 +62,7 @@ class LogoutResponseDto {
|
|||||||
|
|
||||||
return LogoutResponseDto(
|
return LogoutResponseDto(
|
||||||
successful: mapValueOfType<bool>(json, r'successful')!,
|
successful: mapValueOfType<bool>(json, r'successful')!,
|
||||||
|
redirectUri: mapValueOfType<String>(json, r'redirectUri')!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -106,6 +113,7 @@ class LogoutResponseDto {
|
|||||||
/// The list of required keys that must be present in a JSON.
|
/// The list of required keys that must be present in a JSON.
|
||||||
static const requiredKeys = <String>{
|
static const requiredKeys = <String>{
|
||||||
'successful',
|
'successful',
|
||||||
|
'redirectUri',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user