diff --git a/backend/server/integrations/views.py b/backend/server/integrations/views.py index 673fad5..935e28c 100644 --- a/backend/server/integrations/views.py +++ b/backend/server/integrations/views.py @@ -150,6 +150,88 @@ class ImmichIntegrationView(viewsets.ViewSet): }, status=status.HTTP_503_SERVICE_UNAVAILABLE ) + + @action(detail=False, methods=['get']) + def albums(self, request): + """ + RESTful GET method for retrieving all Immich albums. + """ + # Check for integration before proceeding + integration = self.check_integration(request) + if isinstance(integration, Response): + return integration + + # 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.get(f'{integration.server_url}/albums', headers={ + 'x-api-key': integration.api_key + }) + res = immich_fetch.json() + except requests.exceptions.ConnectionError: + return Response( + { + 'message': 'The Immich server is currently down or unreachable.', + 'error': True, + 'code': 'immich.server_down' + }, + status=status.HTTP_503_SERVICE_UNAVAILABLE + ) + + return Response( + res, + status=status.HTTP_200_OK + ) + + @action(detail=False, methods=['get'], url_path='albums/(?P[^/.]+)') + def album(self, request, albumid=None): + """ + RESTful GET method for retrieving a specific Immich album by ID. + """ + # Check for integration before proceeding + integration = self.check_integration(request) + if isinstance(integration, Response): + return integration + + if not albumid: + return Response( + { + 'message': 'Album ID is required.', + 'error': True, + 'code': 'immich.albumid_required' + }, + status=status.HTTP_400_BAD_REQUEST + ) + + # 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.get(f'{integration.server_url}/albums/{albumid}', headers={ + 'x-api-key': integration.api_key + }) + res = immich_fetch.json() + except requests.exceptions.ConnectionError: + return Response( + { + 'message': 'The Immich server is currently down or unreachable.', + 'error': True, + 'code': 'immich.server_down' + }, + status=status.HTTP_503_SERVICE_UNAVAILABLE + ) + + if 'assets' in res: + return Response( + res['assets'], + status=status.HTTP_200_OK + ) + else: + return Response( + { + 'message': 'No assets found in this album.', + 'error': True, + 'code': 'immich.no_assets_found' + }, + status=status.HTTP_404_NOT_FOUND + ) class ImmichIntegrationViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated] diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index d5e4d89..550c419 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -13,7 +13,6 @@ import { addToast } from '$lib/toasts'; import { deserialize } from '$app/forms'; import { t } from 'svelte-i18n'; - import ImmichLogo from '$lib/assets/immich.svg'; export let longitude: number | null = null; export let latitude: number | null = null; export let collection: Collection | null = null; @@ -33,6 +32,7 @@ import CategoryDropdown from './CategoryDropdown.svelte'; import { findFirstValue } from '$lib'; import MarkdownEditor from './MarkdownEditor.svelte'; + import ImmichSelect from './ImmichSelect.svelte'; let wikiError: string = ''; @@ -207,7 +207,7 @@ // Assuming the first object in the array is the new image let newImage = { - id: rawData[0].id, + id: rawData[1], image: rawData[2] // This is the URL for the image }; console.log('New Image:', newImage); @@ -217,6 +217,7 @@ adventure.images = images; addToast('success', $t('adventures.image_upload_success')); + url = ''; } else { addToast('error', $t('adventures.image_upload_error')); } @@ -226,68 +227,6 @@ } } - let immichSearchValue: string = ''; - let immichError: string = ''; - let immichNext: string = ''; - let immichPage: number = 1; - - 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 = ''; - } - } - } - - 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 = ''; - } - } - } - async function fetchWikiImage() { let res = await fetch(`/api/generate/img/?name=${imageSearch}`); let data = await res.json(); @@ -421,7 +360,6 @@ let modal: HTMLDialogElement; let immichIntegration: boolean = false; - let immichImages: any[] = []; onMount(async () => { modal = document.getElementById('my_modal_1') as HTMLDialogElement; @@ -1078,52 +1016,12 @@ it would also work to just use on:click on the MapLibre component itself. --> {#if immichIntegration} -
- - -
- - -
-

{immichError}

-
- {#each immichImages as image} -
- - Image from Immich - -
- {/each} - {#if immichNext} - - {/if} -
-
+ { + url = e.detail; + fetchImage(); + }} + /> {/if}
diff --git a/frontend/src/lib/components/ImmichSelect.svelte b/frontend/src/lib/components/ImmichSelect.svelte new file mode 100644 index 0000000..d5602b9 --- /dev/null +++ b/frontend/src/lib/components/ImmichSelect.svelte @@ -0,0 +1,159 @@ + + +
+ +
+ + + {#if searchOrSelect === 'search'} + + + {:else} + + {/if} +
+ +

{immichError}

+
+ {#each immichImages as image} +
+ + Image from Immich + +
+ {/each} + {#if immichNext} + + {/if} +
+
diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 75cee6e..a2e6d3c 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -202,3 +202,31 @@ export type ImmichIntegration = { server_url: string; api_key: string; }; + +export type ImmichAlbum = { + albumName: string; + description: string; + albumThumbnailAssetId: string; + createdAt: string; + updatedAt: string; + id: string; + ownerId: string; + owner: { + id: string; + email: string; + name: string; + profileImagePath: string; + avatarColor: string; + profileChangedAt: string; + }; + albumUsers: any[]; + shared: boolean; + hasSharedLink: boolean; + startDate: string; + endDate: string; + assets: any[]; + assetCount: number; + isActivityEnabled: boolean; + order: string; + lastModifiedAssetTimestamp: string; +};