2024-10-28 20:47:14 +01:00
|
|
|
import { Asset, AssetType, ImageSize, SharedLink } from './types'
|
2024-10-29 19:22:18 +01:00
|
|
|
import dayjs from 'dayjs'
|
2024-10-30 14:43:26 +01:00
|
|
|
import { log } from './index'
|
2024-10-28 09:56:11 +01:00
|
|
|
|
2024-10-28 09:08:16 +01:00
|
|
|
class Immich {
|
2024-10-29 15:07:54 +01:00
|
|
|
/**
|
|
|
|
* Make a request to Immich API. We're not using the SDK to limit
|
|
|
|
* the possible attack surface of this app.
|
|
|
|
*/
|
2024-10-29 10:29:14 +01:00
|
|
|
async request (endpoint: string) {
|
2024-10-28 09:08:16 +01:00
|
|
|
const res = await fetch(process.env.IMMICH_URL + '/api' + endpoint, {
|
|
|
|
headers: {
|
2024-10-28 09:56:11 +01:00
|
|
|
'x-api-key': process.env.API_KEY || ''
|
2024-10-28 09:08:16 +01:00
|
|
|
}
|
|
|
|
})
|
2024-10-28 20:47:14 +01:00
|
|
|
if (res.status === 200) {
|
2024-10-29 10:29:14 +01:00
|
|
|
const contentType = res.headers.get('Content-Type') || ''
|
|
|
|
if (contentType.includes('application/json')) {
|
2024-10-28 20:47:14 +01:00
|
|
|
return res.json()
|
|
|
|
} else {
|
|
|
|
return res
|
|
|
|
}
|
2024-10-30 17:59:14 +01:00
|
|
|
} else {
|
|
|
|
log('Immich API status ' + res.status)
|
|
|
|
console.log(await res.text())
|
2024-10-28 09:08:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-29 15:07:54 +01:00
|
|
|
/**
|
|
|
|
* Query Immich for the SharedLink metadata for a given key.
|
|
|
|
* The key is what is returned in the URL when you create a share in Immich.
|
2024-10-30 12:34:04 +01:00
|
|
|
*
|
2024-10-29 15:07:54 +01:00
|
|
|
* Immich doesn't have a method to query by key, so this method gets all
|
|
|
|
* known shared links, and returns the link which matches the provided key.
|
|
|
|
*/
|
2024-10-28 09:56:11 +01:00
|
|
|
async getShareByKey (key: string) {
|
2024-10-29 10:29:14 +01:00
|
|
|
const res = (await this.request('/shared-links') || []) as SharedLink[]
|
2024-10-29 19:22:18 +01:00
|
|
|
const link = res.find(x => x.key === key)
|
|
|
|
if (link) {
|
|
|
|
// Filter assets to exclude trashed assets
|
|
|
|
link.assets = link.assets.filter(x => !x.isTrashed)
|
|
|
|
if (link.expiresAt && dayjs(link.expiresAt) < dayjs()) {
|
|
|
|
// This link has expired
|
2024-10-30 14:43:26 +01:00
|
|
|
log('Expired link ' + key)
|
2024-10-29 19:22:18 +01:00
|
|
|
} else {
|
|
|
|
return link
|
|
|
|
}
|
|
|
|
}
|
2024-10-28 09:08:16 +01:00
|
|
|
}
|
|
|
|
|
2024-10-29 15:07:54 +01:00
|
|
|
/**
|
|
|
|
* Stream asset buffer data from Immich.
|
2024-10-30 12:34:04 +01:00
|
|
|
*
|
2024-10-29 15:07:54 +01:00
|
|
|
* For photos, you can request 'thumbnail' or 'original' size.
|
|
|
|
* For videos, it is Immich's streaming quality, not the original quality.
|
|
|
|
*/
|
2024-10-28 20:47:14 +01:00
|
|
|
async getAssetBuffer (asset: Asset, size?: ImageSize) {
|
|
|
|
switch (asset.type) {
|
|
|
|
case AssetType.image:
|
|
|
|
size = size === ImageSize.thumbnail ? ImageSize.thumbnail : ImageSize.original
|
2024-10-29 20:44:46 +01:00
|
|
|
return this.request('/assets/' + encodeURIComponent(asset.id) + '/' + size)
|
2024-10-28 20:47:14 +01:00
|
|
|
case AssetType.video:
|
2024-10-29 20:44:46 +01:00
|
|
|
return this.request('/assets/' + encodeURIComponent(asset.id) + '/video/playback')
|
2024-10-28 20:47:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-29 15:07:54 +01:00
|
|
|
/**
|
|
|
|
* Get the content-type of an Immich asset
|
|
|
|
*/
|
2024-10-29 10:29:14 +01:00
|
|
|
async getContentType (asset: Asset) {
|
|
|
|
const assetBuffer = await this.getAssetBuffer(asset)
|
|
|
|
return assetBuffer.headers.get('Content-Type')
|
|
|
|
}
|
|
|
|
|
2024-10-29 15:07:54 +01:00
|
|
|
/**
|
|
|
|
* Return the image data URL for a photo
|
|
|
|
*/
|
|
|
|
photoUrl (key: string, id: string, size?: ImageSize) {
|
2024-10-29 19:57:44 +01:00
|
|
|
return `/photo/${key}/${id}` + (size ? `?size=${size}` : '')
|
2024-10-28 09:08:16 +01:00
|
|
|
}
|
2024-10-29 10:29:14 +01:00
|
|
|
|
2024-10-29 15:07:54 +01:00
|
|
|
/**
|
|
|
|
* Return the video data URL for a video
|
|
|
|
*/
|
|
|
|
videoUrl (key: string, id: string) {
|
2024-10-29 19:57:44 +01:00
|
|
|
return `/video/${key}/${id}`
|
2024-10-29 10:29:14 +01:00
|
|
|
}
|
|
|
|
|
2024-10-29 15:07:54 +01:00
|
|
|
/**
|
|
|
|
* Check if a provided ID matches the Immich ID format
|
|
|
|
*/
|
2024-10-29 10:29:14 +01:00
|
|
|
isId (id: string) {
|
|
|
|
return !!id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)
|
|
|
|
}
|
2024-10-29 15:07:54 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a provided key matches the Immich shared-link key format
|
|
|
|
*/
|
|
|
|
isKey (key: string) {
|
|
|
|
return !!key.match(/^[\w-]+$/)
|
|
|
|
}
|
2024-10-28 09:08:16 +01:00
|
|
|
}
|
|
|
|
|
2024-10-29 10:29:14 +01:00
|
|
|
const immich = new Immich()
|
2024-10-28 09:56:11 +01:00
|
|
|
|
2024-10-29 10:29:14 +01:00
|
|
|
export default immich
|