mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-25 07:49:37 +02:00
Merge pull request #478 from Thiesjoo/immich-improvements
Immich image selection improvements
This commit is contained in:
commit
bdb17a3177
15 changed files with 222 additions and 133 deletions
|
@ -63,25 +63,31 @@ class ImmichIntegrationView(viewsets.ViewSet):
|
|||
return integration
|
||||
|
||||
query = request.query_params.get('query', '')
|
||||
date = request.query_params.get('date', '')
|
||||
|
||||
if not query:
|
||||
if not query and not date:
|
||||
return Response(
|
||||
{
|
||||
'message': 'Query is required.',
|
||||
'message': 'Query or date is required.',
|
||||
'error': True,
|
||||
'code': 'immich.query_required'
|
||||
},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
arguments = {}
|
||||
if query:
|
||||
arguments['query'] = query
|
||||
if date:
|
||||
arguments['takenBefore'] = date
|
||||
|
||||
# check so if the server is down, it does not tweak out like a madman and crash the server with a 500 error code
|
||||
try:
|
||||
immich_fetch = requests.post(f'{integration.server_url}/search/smart', headers={
|
||||
url = f'{integration.server_url}/search/{"smart" if query else "metadata"}'
|
||||
immich_fetch = requests.post(url, headers={
|
||||
'x-api-key': integration.api_key
|
||||
},
|
||||
json = {
|
||||
'query': query
|
||||
}
|
||||
json = arguments
|
||||
)
|
||||
res = immich_fetch.json()
|
||||
except requests.exceptions.ConnectionError:
|
||||
|
@ -219,10 +225,14 @@ class ImmichIntegrationView(viewsets.ViewSet):
|
|||
)
|
||||
|
||||
if 'assets' in res:
|
||||
return Response(
|
||||
res['assets'],
|
||||
status=status.HTTP_200_OK
|
||||
)
|
||||
paginator = self.pagination_class()
|
||||
# for each item in the items, we need to add the image url to the item so we can display it in the frontend
|
||||
public_url = os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/')
|
||||
public_url = public_url.replace("'", "")
|
||||
for item in res['assets']:
|
||||
item['image_url'] = f'{public_url}/api/integrations/immich/get/{item["id"]}'
|
||||
result_page = paginator.paginate_queryset(res['assets'], request)
|
||||
return paginator.get_paginated_response(result_page)
|
||||
else:
|
||||
return Response(
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ I’m thrilled to announce the release of **AdventureLog v0.8.0**, a huge update
|
|||
### 🚗 Transportation
|
||||
|
||||
- **New Transportation Edit Modal**: Includes detailed origin and destination location information for better trip planning.
|
||||
- **Autocomplete for Airport Codes**: Quickly find and add airport codes while planning transportations.
|
||||
- **Autocomplete for Airport Codes**: Quickly find and add airport codes while planning transportation.
|
||||
- **New Transportation Card Design**: Redesigned for better clarity and aesthetics.
|
||||
|
||||
---
|
||||
|
@ -27,7 +27,7 @@ I’m thrilled to announce the release of **AdventureLog v0.8.0**, a huge update
|
|||
### 📝 Notes and Checklists
|
||||
|
||||
- **New Modals for Notes and Checklists**: Simplified creation and editing of your notes and checklists.
|
||||
- **Delete Confirmation**: Added a confirmation step when deleting notes, checklists, or transportations to prevent accidental deletions.
|
||||
- **Delete Confirmation**: Added a confirmation step when deleting notes, checklists, or transportation to prevent accidental deletions.
|
||||
|
||||
---
|
||||
|
||||
|
@ -41,7 +41,7 @@ I’m thrilled to announce the release of **AdventureLog v0.8.0**, a huge update
|
|||
|
||||
### 🗓️ Calendar
|
||||
|
||||
- **Calendar View**: View your adventures and transportations in a calendar layout.
|
||||
- **Calendar View**: View your adventures and transportation in a calendar layout.
|
||||
- **ICS File Export**: Export your calendar as an ICS file for use with external apps like Google Calendar or Outlook.
|
||||
|
||||
---
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Social Authentication
|
||||
|
||||
AdventureLog support autentication via 3rd party services and self-hosted identity providers. Once these services are enabled, users can log in to AdventureLog using their accounts from these services and link exising AdventureLog accounts to these services for easier access.
|
||||
AdventureLog support authentication via 3rd party services and self-hosted identity providers. Once these services are enabled, users can log in to AdventureLog using their accounts from these services and link existing AdventureLog accounts to these services for easier access.
|
||||
|
||||
The steps for each service varies so please refer to the specific service's documentation for more information.
|
||||
|
||||
|
|
|
@ -22,14 +22,14 @@ To enable Authentik as an identity provider, the administrator must first config
|
|||
|
||||
### AdventureLog Configuration
|
||||
|
||||
This configuration is done in the [Admin Panel](../../guides/admin_panel.md). You can either launch the pannel directly from the `Settings` page or navigate to `/admin` on your AdventureLog server.
|
||||
This configuration is done in the [Admin Panel](../../guides/admin_panel.md). You can either launch the panel directly from the `Settings` page or navigate to `/admin` on your AdventureLog server.
|
||||
|
||||
1. Login to AdventureLog as an administrator and navigate to the `Settings` page.
|
||||
2. Scroll down to the `Administration Settings` and launch the admin panel.
|
||||
3. In the admin panel, navigate to the `Social Accounts` section and click the add button next to `Social applications`. Fill in the following fields:
|
||||
|
||||
- Provider: `OpenID Connect`
|
||||
- Provider ID: Autnentik Client ID
|
||||
- Provider ID: Authentik Client ID
|
||||
- Name: `Authentik`
|
||||
- Client ID: Authentik Client ID
|
||||
- Secret Key: Authentik Client Secret
|
||||
|
|
|
@ -20,7 +20,7 @@ To enable GitHub as an identity provider, the administrator must first configure
|
|||
|
||||
### AdventureLog Configuration
|
||||
|
||||
This configuration is done in the [Admin Panel](../../guides/admin_panel.md). You can either launch the pannel directly from the `Settings` page or navigate to `/admin` on your AdventureLog server.
|
||||
This configuration is done in the [Admin Panel](../../guides/admin_panel.md). You can either launch the panel directly from the `Settings` page or navigate to `/admin` on your AdventureLog server.
|
||||
|
||||
1. Login to AdventureLog as an administrator and navigate to the `Settings` page.
|
||||
2. Scroll down to the `Administration Settings` and launch the admin panel.
|
||||
|
@ -41,4 +41,4 @@ This configuration is done in the [Admin Panel](../../guides/admin_panel.md). Yo
|
|||
|
||||
4. Save the configuration.
|
||||
|
||||
Users should now be able to log in to AdventureLog using their GitHub account, and link it to exisiting accounts.
|
||||
Users should now be able to log in to AdventureLog using their GitHub account, and link it to existing accounts.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Updating
|
||||
|
||||
Updating AdventureLog when using docker can be quite easy. Run the folowing commands to pull the latest version and restart the containers. Make sure you backup your instance before updating just in case!
|
||||
Updating AdventureLog when using docker can be quite easy. Run the following commands to pull the latest version and restart the containers. Make sure you backup your instance before updating just in case!
|
||||
|
||||
Note: Make sure you are in the same directory as your `docker-compose.yml` file.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# AdventureLog v0.7.1 Migration
|
||||
|
||||
In order to make installation easier, the AdventureLog v0.7.1 release has **removed the need for a seperate nginx container** and cofig to serve the media files. Instead, the media files are now served by an instance of nginx running in the same container as the Django application.
|
||||
In order to make installation easier, the AdventureLog v0.7.1 release has **removed the need for a separate nginx container** and config to serve the media files. Instead, the media files are now served by an instance of nginx running in the same container as the Django application.
|
||||
|
||||
## Docker Compose Changes
|
||||
|
||||
|
|
|
@ -25,31 +25,31 @@ wget https://raw.githubusercontent.com/seanmorley15/AdventureLog/main/docker-com
|
|||
|
||||
Here is a summary of the configuration options available in the `docker-compose.yml` file:
|
||||
|
||||
<!-- make a table with colum name, is required, other -->
|
||||
<!-- make a table with column name, is required, other -->
|
||||
|
||||
### Frontend Container (web)
|
||||
|
||||
| Name | Required | Description | Default Value |
|
||||
| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
|
||||
| `PUBLIC_SERVER_URL` | Yes | What the frontend SSR server uses to connect to the backend. | http://server:8000 |
|
||||
| `ORIGIN` | Sometimes | Not needed if using HTTPS. If not, set it to the domain of what you will acess the app from. | http://localhost:8015 |
|
||||
| `BODY_SIZE_LIMIT` | Yes | Used to set the maximum upload size to the server. Should be changed to prevent someone from uploading too much! Custom values must be set in **kiliobytes**. | Infinity |
|
||||
| Name | Required | Description | Default Value |
|
||||
| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
|
||||
| `PUBLIC_SERVER_URL` | Yes | What the frontend SSR server uses to connect to the backend. | ```http://server:8000``` |
|
||||
| `ORIGIN` | Sometimes | Not needed if using HTTPS. If not, set it to the domain of what you will access the app from. | ```http://localhost:8015``` |
|
||||
| `BODY_SIZE_LIMIT` | Yes | Used to set the maximum upload size to the server. Should be changed to prevent someone from uploading too much! Custom values must be set in **kilobytes**. | ```Infinity``` |
|
||||
|
||||
### Backend Container (server)
|
||||
|
||||
| Name | Required | Description | Default Value |
|
||||
| ----------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
|
||||
| `PGHOST` | Yes | Databse host. | db |
|
||||
| `PGDATABASE` | Yes | Database. | database |
|
||||
| `PGUSER` | Yes | Database user. | adventure |
|
||||
| `PGPASSWORD` | Yes | Database password. | changeme123 |
|
||||
| `PGPORT` | No | Database port. | 5432 |
|
||||
| `DJANGO_ADMIN_USERNAME` | Yes | Default username. | admin |
|
||||
| `DJANGO_ADMIN_PASSWORD` | Yes | Default password, change after inital login. | admin |
|
||||
| `DJANGO_ADMIN_EMAIL` | Yes | Default user's email. | admin@example.com |
|
||||
| `PUBLIC_URL` | Yes | This needs to match the outward port of the server and be accessible from where the app is used. It is used for the creation of image urls. | http://localhost:8016 |
|
||||
| `CSRF_TRUSTED_ORIGINS` | Yes | Need to be changed to the orgins where you use your backend server and frontend. These values are comma seperated. | http://localhost:8016 |
|
||||
| `FRONTEND_URL` | Yes | This is the publically accessible url to the **frontend** container. This link should be accessable for all users. Used for email generation. | http://localhost:8015 |
|
||||
| Name | Required | Description | Default Value |
|
||||
| ----------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
|
||||
| `PGHOST` | Yes | Database host. | ```db``` |
|
||||
| `PGDATABASE` | Yes | Database. | ```database``` |
|
||||
| `PGUSER` | Yes | Database user. | ```adventure``` |
|
||||
| `PGPASSWORD` | Yes | Database password. | ```changeme123``` |
|
||||
| `PGPORT` | No | Database port. | ```5432``` |
|
||||
| `DJANGO_ADMIN_USERNAME` | Yes | Default username. | ```admin``` |
|
||||
| `DJANGO_ADMIN_PASSWORD` | Yes | Default password, change after initial login. | ```admin``` |
|
||||
| `DJANGO_ADMIN_EMAIL` | Yes | Default user's email. | ```admin@example.com``` |
|
||||
| `PUBLIC_URL` | Yes | This needs to match the outward port of the server and be accessible from where the app is used. It is used for the creation of image urls. | ```http://localhost:8016``` |
|
||||
| `CSRF_TRUSTED_ORIGINS` | Yes | Need to be changed to the origins where you use your backend server and frontend. These values are comma separated. | ```http://localhost:8016``` |
|
||||
| `FRONTEND_URL` | Yes | This is the publicly accessible url to the **frontend** container. This link should be accessible for all users. Used for email generation. | ```http://localhost:8015``` |
|
||||
|
||||
## Running the Containers
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ You must [expose tailnet IPs to your cluster](https://tailscale.com/kb/1438/kube
|
|||
|
||||
## Getting Started
|
||||
|
||||
Take a look at the [example config](https://github.com/seanmorley15/AdventureLog/blob/main/kustomization.yml) and modify it for your usecase.
|
||||
Take a look at the [example config](https://github.com/seanmorley15/AdventureLog/blob/main/kustomization.yml) and modify it for your use case.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ Ensure that the Nginx Proxy Manager and AdventureLog containers are on the same
|
|||
docker network create nginx-proxy-manager
|
||||
```
|
||||
|
||||
Add the folowing to the bottom of the `docker-compose.yml` file for the Nginx Proxy Manager service and the AdventureLog service.
|
||||
Add the following to the bottom of the `docker-compose.yml` file for the Nginx Proxy Manager service and the AdventureLog service.
|
||||
|
||||
```yaml
|
||||
networks:
|
||||
|
|
|
@ -1,29 +1,71 @@
|
|||
# Installation with Unraid
|
||||
|
||||
AdventureLog is available in the Unraid Community Applications store. You can install it by searching for "AdventureLog" in the Community Applications store.
|
||||
AdventureLog is available in the Unraid Community Applications store. You can install it by searching for `AdventureLog` in the Community Applications store, where you will find the frontend and the backend. The database can be found by searching `PostGIS`.
|
||||
|
||||
Community Applications Page: [AdventureLog on CA Store](https://unraid.net/community/apps?q=Adventurelog)
|
||||
Community Applications Page for AdventureLog: [AdventureLog on CA Store](https://unraid.net/community/apps?q=AdventureLog)\
|
||||
Community Applications Page for PostGIS: [PostGIS on CA Store](https://unraid.net/community/apps?q=PostGIS)
|
||||
|
||||
## Installation Configuration
|
||||
|
||||
It is recommended to install applications in this order.
|
||||
- **Note:** It is recommended to install the applications in the order of these instructions, as failing to do so could cause issues.\
|
||||
- Container names can be set to whatever you desire.
|
||||
- Also ensure they are all on the same custom network so they can communicate with one another. You can create one by running the following command in your command line, with `example` being set to your desired name. This network will then show up for selection when making the apps/containers.
|
||||
|
||||
```bash
|
||||
docker network create example
|
||||
```
|
||||
|
||||
## Database
|
||||
|
||||
- To find the Database Application, search for `PostGIS` on the Unraid App Store and fill out the fields as follows:
|
||||
- Ensure that the POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB are set in the PostGIS container if not add them custom variables
|
||||
- Network type should be set to your **custom network**.
|
||||
- There is **no** AdventureLog---Database app, to find the database application search for `PostGIS` on the Unraid App Store then add and fill out the fields as shown below
|
||||
- Change the repository version to `postgis/postgis:15-3.3`
|
||||
- Ensure that the variables ```POSTGRES_DB```, ```POSTGRES_USER```, and ```POSTGRES_PASSWORD``` are set in the ```PostGIS``` container. If not, then add them as custom variables. The template should have ```POSTGRES_PASSWORD``` already and you will simply have to add ```POSTGRES_DB``` and ```POSTGRES_USER```.
|
||||
- The forwarded port of ```5012``` is not needed unless you plan to access the database outside of the container's network.
|
||||
|
||||

|
||||
| Name | Required | Description | Default Value |
|
||||
| ------------------- | -------- | -------------------------------------------------------------------------------- | --------------- |
|
||||
| `POSTGRES_DB` | Yes | The name of the database in PostGIS. | `N/A` |
|
||||
| `POSTGRES_USER` | Yes | Name of the user generated on first start that will have access to the database. | `N/A` |
|
||||
| `POSTGRES_PASSWORD` | Yes | Password of the user that will be generated on first start. | `N/A` |
|
||||
|
||||
- Here's some visual instructions of how to configure the database template, click the image to open larger version in new tab.\
|
||||
[](/unraid-config-2.png)
|
||||
|
||||
## Backend
|
||||
|
||||
- Cache Configuration: This option is useful only if your appdata share is stored on a cache drive, which is used to speed up read/write operations for your containerized applications.
|
||||
- Note: if your running the server in a docker network that is other than "host" (for example "bridge") than you need to add the IP of the host machine in the CSRF Trusted Origins variable.
|
||||
- Network type should be set to your **custom network**.
|
||||
- **Note:** If you're running the server in a docker network that is other than "host" (for example "bridge"), then you need to add the IP of the host machine in the CSRF Trusted Origins variable instead of using localhost. This is only necessary when accessing locally, otherwise you will use the domain name.
|
||||
|
||||

|
||||
| Name | Required | Description | Default Value |
|
||||
| ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
||||
| `API Port` | Yes | This is the port of the backend. This is a port, not a variable. | `8016` |
|
||||
| `PGHOST` | Yes | This is how the backend will access the database. Use the database container's name. | `N/A` |
|
||||
| `PGDATABASE` | Yes | Name of the database in PostGIS to access. | `N/A` |
|
||||
| `PGUSER` | Yes | Name of the user to access with. This is the same as the variable in the database. | `N/A` |
|
||||
| `PGPASSWORD` | Yes | Password of the user it's accessing with. This is the same as the variable in the database. | `N/A` |
|
||||
| `SECRET_KEY` | Yes | Secret Backend Key. Change to anything. | `N/A` |
|
||||
| `DJANGO_ADMIN_USERNAME` | Yes | Default username for admin access. | `admin` |
|
||||
| `DJANGO_ADMIN_EMAIL` | Yes | Default admin user's email. **Note:** You cannot make more than one user with each email. | `N/A` |
|
||||
| `DJANGO_ADMIN_PASSWORD` | Yes | Default password for admin access. Change after initial login. | `N/A` |
|
||||
| `PUBLIC_URL` | Yes | This needs to match how you will connect to the backend, so either local ip with matching port or domain. It is used for the creation of image URLs. | `http://IP_ADDRESS:8016` |
|
||||
| `FRONTEND_URL` | Yes | This needs to match how you will connect to the frontend, so either local ip with matching port or domain. This link should be available for all users. Used for email generation. | `http://IP_ADDRESS:8015` |
|
||||
| `CSRF_TRUSTED_ORIGINS` | Yes | This needs to be changed to the URLs of how you connect to your backend server and frontend. These values are comma-separated and usually the same as the 2 above values. | `http://IP_ADDRESS:8016,http://IP_ADDRESS:8015` |
|
||||
|
||||
- Here's some visual instructions of how to configure the backend template, click the image to open larger version in new tab.\
|
||||
[](/unraid-config-1.png)
|
||||
|
||||
## Frontend
|
||||
|
||||
- By default, the frontend connects to the backend using `http://server:8000`. This will work if both the frontend and backend are on the same network. Otherwise, you’ll need to configure it to use the exposed port (default: 8016).
|
||||
- Network type should be set to your **custom network**.
|
||||
- **Note:** The default value for ```PUBLIC_SERVER_URL``` is ```http://IP_ADDRESS:8000```, however ```IP_ADDRESS``` **should be changed** to the name of the backend container for simplicity.
|
||||
|
||||

|
||||
| Name | Required | Description | Default Value |
|
||||
| ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ |
|
||||
| `WEB UI Port` | Yes | The port of the frontend. This is not a variable. | `8015` |
|
||||
| `PUBLIC_SERVER_URL` | Yes | What the frontend SSR server uses to connect to the backend. Change `IP_ADDRESS` to the name of the backend container. | `http://IP_ADDRESS:8000` |
|
||||
| `ORIGIN` | Sometimes| Set to the URL you will access the frontend from, such as localhost with correct port, or set it to the domain of what you will access the app from. | `http://IP_ADDRESS:8015` |
|
||||
| `BODY_SIZE_LIMIT` | Yes | Used to set the maximum upload size to the server. Should be changed to prevent someone from uploading too much! Custom values must be set in **kilobytes**. | `Infinity` |
|
||||
|
||||
- Here's some visual instructions of how to configure the frontend template, click the image to open larger version in new tab.\
|
||||
[](/unraid-config-3.png)
|
||||
|
|
|
@ -1257,6 +1257,7 @@ it would also work to just use on:click on the MapLibre component itself. -->
|
|||
|
||||
{#if immichIntegration}
|
||||
<ImmichSelect
|
||||
adventure={adventure}
|
||||
on:fetchImage={(e) => {
|
||||
url = e.detail;
|
||||
fetchImage();
|
||||
|
|
|
@ -1,32 +1,77 @@
|
|||
<script lang="ts">
|
||||
let immichSearchValue: string = '';
|
||||
let searchOrSelect: string = 'search';
|
||||
let immichError: string = '';
|
||||
let immichNext: string = '';
|
||||
let immichPage: number = 1;
|
||||
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import ImmichLogo from '$lib/assets/immich.svg';
|
||||
import type { Adventure, ImmichAlbum } from '$lib/types';
|
||||
import { debounce } from '$lib';
|
||||
|
||||
let immichImages: any[] = [];
|
||||
let immichSearchValue: string = '';
|
||||
let searchCategory: 'search' | 'date' | 'album' = 'date';
|
||||
let immichError: string = '';
|
||||
let immichNextURL: string = '';
|
||||
let loading = false;
|
||||
|
||||
export let adventure: Adventure | null = null;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let albums: ImmichAlbum[] = [];
|
||||
let currentAlbum: string = '';
|
||||
|
||||
let selectedDate: string = (adventure as Adventure | null)?.visits.map(v => new Date(v.end_date || v.start_date)).sort((a,b) => +b - +a)[0]?.toISOString()?.split('T')[0] || '';
|
||||
if (!selectedDate) {
|
||||
selectedDate = new Date().toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
|
||||
$: {
|
||||
if (currentAlbum) {
|
||||
immichImages = [];
|
||||
fetchAlbumAssets(currentAlbum);
|
||||
} else {
|
||||
immichImages = [];
|
||||
} else if (searchCategory === 'date' && selectedDate) {
|
||||
searchImmich();
|
||||
}
|
||||
}
|
||||
|
||||
async function loadMoreImmich() {
|
||||
// The next URL returned by our API is a absolute url to API, but we need to use the relative path, to use the frontend api proxy.
|
||||
const url = new URL(immichNextURL);
|
||||
immichNextURL = url.pathname + url.search;
|
||||
return fetchAssets(immichNextURL, true);
|
||||
}
|
||||
|
||||
async function fetchAssets(url: string, usingNext = false) {
|
||||
loading = true;
|
||||
try {
|
||||
let res = await fetch(url);
|
||||
immichError = '';
|
||||
if (!res.ok) {
|
||||
let data = await res.json();
|
||||
let errorMessage = data.message;
|
||||
console.error('Error in handling fetchAsstes', errorMessage);
|
||||
immichError = $t(data.code);
|
||||
} else {
|
||||
let data = await res.json();
|
||||
if (data.results && data.results.length > 0) {
|
||||
if (usingNext) {
|
||||
immichImages = [...immichImages, ...data.results];
|
||||
} else {
|
||||
immichImages = data.results;
|
||||
}
|
||||
} else {
|
||||
immichError = $t('immich.no_items_found');
|
||||
}
|
||||
|
||||
immichNextURL = data.next || '';
|
||||
}
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAlbumAssets(album_id: string) {
|
||||
let res = await fetch(`/api/integrations/immich/albums/${album_id}`);
|
||||
if (res.ok) {
|
||||
let data = await res.json();
|
||||
immichNext = '';
|
||||
immichImages = data;
|
||||
}
|
||||
async function fetchAlbumAssets(album_id: string,) {
|
||||
return fetchAssets(`/api/integrations/immich/albums/${album_id}`);
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
|
@ -37,67 +82,25 @@
|
|||
}
|
||||
});
|
||||
|
||||
let immichImages: any[] = [];
|
||||
import { t } from 'svelte-i18n';
|
||||
import ImmichLogo from '$lib/assets/immich.svg';
|
||||
import type { ImmichAlbum } from '$lib/types';
|
||||
|
||||
async function searchImmich() {
|
||||
let res = await fetch(`/api/integrations/immich/search/?query=${immichSearchValue}`);
|
||||
if (!res.ok) {
|
||||
let data = await res.json();
|
||||
let errorMessage = data.message;
|
||||
console.log(errorMessage);
|
||||
immichError = $t(data.code);
|
||||
} else {
|
||||
let data = await res.json();
|
||||
console.log(data);
|
||||
immichError = '';
|
||||
if (data.results && data.results.length > 0) {
|
||||
immichImages = data.results;
|
||||
} else {
|
||||
immichError = $t('immich.no_items_found');
|
||||
}
|
||||
if (data.next) {
|
||||
immichNext =
|
||||
'/api/integrations/immich/search?query=' +
|
||||
immichSearchValue +
|
||||
'&page=' +
|
||||
(immichPage + 1);
|
||||
} else {
|
||||
immichNext = '';
|
||||
}
|
||||
}
|
||||
|
||||
function buildQueryParams() {
|
||||
let params = new URLSearchParams();
|
||||
if (immichSearchValue && searchCategory === 'search') {
|
||||
params.append('query', immichSearchValue);
|
||||
} else if (selectedDate && searchCategory === 'date') {
|
||||
params.append('date', selectedDate);
|
||||
}
|
||||
return params.toString();
|
||||
}
|
||||
|
||||
async function loadMoreImmich() {
|
||||
let res = await fetch(immichNext);
|
||||
if (!res.ok) {
|
||||
let data = await res.json();
|
||||
let errorMessage = data.message;
|
||||
console.log(errorMessage);
|
||||
immichError = $t(data.code);
|
||||
} else {
|
||||
let data = await res.json();
|
||||
console.log(data);
|
||||
immichError = '';
|
||||
if (data.results && data.results.length > 0) {
|
||||
immichImages = [...immichImages, ...data.results];
|
||||
} else {
|
||||
immichError = $t('immich.no_items_found');
|
||||
}
|
||||
if (data.next) {
|
||||
immichNext =
|
||||
'/api/integrations/immich/search?query=' +
|
||||
immichSearchValue +
|
||||
'&page=' +
|
||||
(immichPage + 1);
|
||||
immichPage++;
|
||||
} else {
|
||||
immichNext = '';
|
||||
}
|
||||
}
|
||||
const searchImmich = debounce(() => {
|
||||
_searchImmich();
|
||||
}, 500); // Debounce the search function to avoid multiple requests on every key press
|
||||
|
||||
async function _searchImmich() {
|
||||
return fetchAssets(`/api/integrations/immich/search/?${buildQueryParams()}`);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="mb-4">
|
||||
|
@ -111,20 +114,27 @@
|
|||
on:click={() => (currentAlbum = '')}
|
||||
type="radio"
|
||||
class="join-item btn"
|
||||
bind:group={searchOrSelect}
|
||||
bind:group={searchCategory}
|
||||
value="search"
|
||||
aria-label="Search"
|
||||
/>
|
||||
<input
|
||||
type="radio"
|
||||
class="join-item btn"
|
||||
bind:group={searchOrSelect}
|
||||
value="select"
|
||||
bind:group={searchCategory}
|
||||
value="date"
|
||||
aria-label="Show by date"
|
||||
/>
|
||||
<input
|
||||
type="radio"
|
||||
class="join-item btn"
|
||||
bind:group={searchCategory}
|
||||
value="album"
|
||||
aria-label="Select Album"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
{#if searchOrSelect === 'search'}
|
||||
{#if searchCategory === 'search'}
|
||||
<form on:submit|preventDefault={searchImmich}>
|
||||
<input
|
||||
type="text"
|
||||
|
@ -134,7 +144,13 @@
|
|||
/>
|
||||
<button type="submit" class="btn btn-neutral mt-2">Search</button>
|
||||
</form>
|
||||
{:else}
|
||||
{:else if searchCategory === 'date'}
|
||||
<input
|
||||
type="date"
|
||||
bind:value={selectedDate}
|
||||
class="input input-bordered w-full max-w-xs mt-2"
|
||||
/>
|
||||
{:else if searchCategory === 'album'}
|
||||
<select class="select select-bordered w-full max-w-xs mt-2" bind:value={currentAlbum}>
|
||||
<option value="" disabled selected>Select an Album</option>
|
||||
{#each albums as album}
|
||||
|
@ -147,14 +163,23 @@
|
|||
|
||||
<p class="text-red-500">{immichError}</p>
|
||||
<div class="flex flex-wrap gap-4 mr-4 mt-2">
|
||||
{#if loading}
|
||||
<div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[100] w-24 h-24">
|
||||
<span class="loading loading-spinner w-24 h-24"></span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#each immichImages as image}
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div class="flex flex-col items-center gap-2" class:blur-sm={loading}>
|
||||
<!-- svelte-ignore a11y-img-redundant-alt -->
|
||||
<img
|
||||
src={`/immich/${image.id}`}
|
||||
alt="Image from Immich"
|
||||
class="h-24 w-24 object-cover rounded-md"
|
||||
/>
|
||||
<h4>
|
||||
{image.fileCreatedAt?.split('T')[0] || "Unknown"}
|
||||
</h4>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-primary"
|
||||
|
@ -168,7 +193,7 @@
|
|||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
{#if immichNext}
|
||||
{#if immichNextURL}
|
||||
<button class="btn btn-neutral" on:click={loadMoreImmich}>{$t('immich.load_more')}</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
// Event listener for focusing input
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === '/' && document.activeElement !== inputElement) {
|
||||
// Ignore any keypresses in an input/textarea field, so we don't interfere with typing.
|
||||
if (event.key === '/' && !["INPUT", "TEXTAREA"].includes((event.target as HTMLElement)?.tagName)) {
|
||||
event.preventDefault(); // Prevent browser's search shortcut
|
||||
if (inputElement) {
|
||||
inputElement.focus();
|
||||
|
|
|
@ -464,3 +464,13 @@ export function osmTagToEmoji(tag: string) {
|
|||
return '📍'; // Default placeholder emoji for unknown tags
|
||||
}
|
||||
}
|
||||
|
||||
export function debounce(func: Function, timeout: number) {
|
||||
let timer: number | NodeJS.Timeout;
|
||||
return (...args: any) => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
func(...args);
|
||||
}, timeout);
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue