1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-23 14:59:36 +02:00

immich: Now able to show images by date!

This commit is contained in:
Thies 2025-01-30 12:18:35 +01:00
parent bc08362a4c
commit aaacacbd60
No known key found for this signature in database
3 changed files with 118 additions and 85 deletions

View file

@ -63,25 +63,31 @@ class ImmichIntegrationView(viewsets.ViewSet):
return integration return integration
query = request.query_params.get('query', '') query = request.query_params.get('query', '')
date = request.query_params.get('date', '')
if not query: if not query and not date:
return Response( return Response(
{ {
'message': 'Query is required.', 'message': 'Query or date is required.',
'error': True, 'error': True,
'code': 'immich.query_required' 'code': 'immich.query_required'
}, },
status=status.HTTP_400_BAD_REQUEST 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 # 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: 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 'x-api-key': integration.api_key
}, },
json = { json = arguments
'query': query
}
) )
res = immich_fetch.json() res = immich_fetch.json()
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
@ -219,10 +225,14 @@ class ImmichIntegrationView(viewsets.ViewSet):
) )
if 'assets' in res: if 'assets' in res:
return Response( paginator = self.pagination_class()
res['assets'], # for each item in the items, we need to add the image url to the item so we can display it in the frontend
status=status.HTTP_200_OK 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: else:
return Response( return Response(
{ {

View file

@ -1,32 +1,69 @@
<script lang="ts"> <script lang="ts">
let immichSearchValue: string = ''; let immichSearchValue: string = '';
let searchOrSelect: string = 'search'; let searchCategory: 'search' | 'date' | 'album' = 'search';
let immichError: string = ''; let immichError: string = '';
let immichNext: string = ''; let immichNextURL: string = '';
let immichPage: number = 1; let loading = false; // TODO: Implement loading indicator
// TODO: Show date of pictures somewhere?
import { createEventDispatcher, onMount } from 'svelte'; import { createEventDispatcher, onMount } from 'svelte';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let albums: ImmichAlbum[] = []; let albums: ImmichAlbum[] = [];
let currentAlbum: string = ''; let currentAlbum: string = '';
let selectedDate: string = new Date().toISOString().split('T')[0]; // TODO: Auto select from adventure.
$: { $: {
if (currentAlbum) { if (currentAlbum) {
immichImages = []; immichImages = [];
fetchAlbumAssets(currentAlbum); fetchAlbumAssets(currentAlbum);
} else { } else if (searchCategory === 'date' && selectedDate) {
immichImages = []; 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 fetchAlbumAssets(album_id: string) { async function fetchAssets(url: string, usingNext = false) {
let res = await fetch(`/api/integrations/immich/albums/${album_id}`); loading = true;
if (res.ok) { try {
let data = await res.json(); let res = await fetch(url);
immichNext = ''; immichError = '';
immichImages = data; 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');
}
if (data.next) {
immichNextURL = data.next;
} else {
immichNextURL = '';
}
}
} finally {
loading = false;
}
}
async function fetchAlbumAssets(album_id: string,) {
return fetchAssets(`/api/integrations/immich/albums/${album_id}`);
} }
onMount(async () => { onMount(async () => {
@ -41,63 +78,26 @@
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import ImmichLogo from '$lib/assets/immich.svg'; import ImmichLogo from '$lib/assets/immich.svg';
import type { ImmichAlbum } from '$lib/types'; import type { ImmichAlbum } from '$lib/types';
import { debounce } from '$lib';
async function searchImmich() { function buildQueryParams() {
let res = await fetch(`/api/integrations/immich/search/?query=${immichSearchValue}`); let params = new URLSearchParams();
if (!res.ok) { if (immichSearchValue && searchCategory === 'search') {
let data = await res.json(); params.append('query', immichSearchValue);
let errorMessage = data.message; } else if (selectedDate && searchCategory === 'date') {
console.log(errorMessage); params.append('date', selectedDate);
immichError = $t(data.code); }
} else { return params.toString();
let data = await res.json(); }
console.log(data);
immichError = ''; const searchImmich = debounce(() => {
if (data.results && data.results.length > 0) { _searchImmich();
immichImages = data.results; }, 500); // Debounce the search function to avoid multiple requests on every key press
} else {
immichError = $t('immich.no_items_found'); async function _searchImmich() {
} return fetchAssets(`/api/integrations/immich/search/?${buildQueryParams()}`);
if (data.next) {
immichNext =
'/api/integrations/immich/search?query=' +
immichSearchValue +
'&page=' +
(immichPage + 1);
} else {
immichNext = '';
}
}
} }
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 = '';
}
}
}
</script> </script>
<div class="mb-4"> <div class="mb-4">
@ -111,20 +111,27 @@
on:click={() => (currentAlbum = '')} on:click={() => (currentAlbum = '')}
type="radio" type="radio"
class="join-item btn" class="join-item btn"
bind:group={searchOrSelect} bind:group={searchCategory}
value="search" value="search"
aria-label="Search" aria-label="Search"
/> />
<input
type="radio"
class="join-item btn"
bind:group={searchCategory}
value="date"
aria-label="Show by date"
/>
<input <input
type="radio" type="radio"
class="join-item btn" class="join-item btn"
bind:group={searchOrSelect} bind:group={searchCategory}
value="select" value="album"
aria-label="Select Album" aria-label="Select Album"
/> />
</div> </div>
<div> <div>
{#if searchOrSelect === 'search'} {#if searchCategory === 'search'}
<form on:submit|preventDefault={searchImmich}> <form on:submit|preventDefault={searchImmich}>
<input <input
type="text" type="text"
@ -134,7 +141,13 @@
/> />
<button type="submit" class="btn btn-neutral mt-2">Search</button> <button type="submit" class="btn btn-neutral mt-2">Search</button>
</form> </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}> <select class="select select-bordered w-full max-w-xs mt-2" bind:value={currentAlbum}>
<option value="" disabled selected>Select an Album</option> <option value="" disabled selected>Select an Album</option>
{#each albums as album} {#each albums as album}
@ -168,7 +181,7 @@
</button> </button>
</div> </div>
{/each} {/each}
{#if immichNext} {#if immichNextURL}
<button class="btn btn-neutral" on:click={loadMoreImmich}>{$t('immich.load_more')}</button> <button class="btn btn-neutral" on:click={loadMoreImmich}>{$t('immich.load_more')}</button>
{/if} {/if}
</div> </div>

View file

@ -464,3 +464,13 @@ export function osmTagToEmoji(tag: string) {
return '📍'; // Default placeholder emoji for unknown tags 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);
};
}