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:
parent
bc08362a4c
commit
aaacacbd60
3 changed files with 118 additions and 85 deletions
|
@ -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(
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
};
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue