feat(immich): an upload with no album ticked lands in the app's own RecipesCam album, made on first use

This commit is contained in:
2026-10-13 07:49:08 +07:00
parent 0e4e5218cf
commit a539f422c5
2 changed files with 92 additions and 3 deletions
+25 -2
View File
@@ -204,6 +204,25 @@ async function listAlbums(server: ImmichServer): Promise<AlbumRow[] | Failure> {
return ((await jsonOf<RawAlbum[]>(res)) ?? []).filter((album) => album.id).map((album) => albumRow(album));
}
// The album this app keeps for its own uploads: made once, on the first upload
// that has no album of its own to go to, then found by name every time after.
// ponytail: found by name, not remembered as an id — an album renamed or deleted
// on Immich is followed (and remade) rather than fought; cache the id in
// `server.albums` if the extra list on every upload ever shows up in a profile.
const APP_ALBUM = 'RecipesCam';
async function appAlbumId(server: ImmichServer): Promise<string | null> {
const albums = await listAlbums(server);
const found = Array.isArray(albums)
? albums.find((album) => album.albumName.trim().toLowerCase() === APP_ALBUM.toLowerCase())
: undefined;
if (found?.id) return found.id;
// A key without `album.create` is refused here, and the photo keeps the
// timeline-only fate it had before: upload is not what failed.
const res = await call(server, { method: 'POST', path: '/albums', json: { albumName: APP_ALBUM } });
if (!res.ok) return null;
return (await jsonOf<RawAlbum>(res))?.id ?? null;
}
// The tags this key can see whose name holds what was typed. Asked for on every
// search rather than cached: a tag made on Immich has to be found by the next
// one, and the list is small. A key without `tag.read` is answered 403, which is
@@ -619,8 +638,12 @@ export function immichRoutes(
const data = await immichRes.json().catch(() => ({}));
const assetId = (data as any)?.id;
// If target album is given, add to album
const targetAlbum = q.album || (server.albums?.length === 1 ? server.albums[0] : undefined);
// Where the photo should land: the album asked for, else the single album
// this account ticked, else the one album the app keeps for itself — made
// on the spot the first time. No key that can hold albums? The photo keeps
// the timeline, as it always did.
const targetAlbum =
q.album || (server.albums?.length === 1 ? server.albums[0] : undefined) || (assetId ? await appAlbumId(server) : undefined);
if (targetAlbum && assetId) {
try {
await fetch(new URL(`${server.url}/api/albums/${targetAlbum}/assets`), {