Skip to content

Commit

Permalink
Merge pull request #115 from varun-raj/pre-release
Browse files Browse the repository at this point in the history
Release 0.13.0
  • Loading branch information
varun-raj authored Jan 19, 2025
2 parents 0c36e06 + 44f64dc commit cd9e620
Show file tree
Hide file tree
Showing 60 changed files with 1,551 additions and 433 deletions.
6 changes: 5 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ SECURE_COOKIE=false # Set to true to enable secure cookies

# Optional
GOOGLE_MAPS_API_KEY="" # Google Maps API Key for heatmap
GEMINI_API_KEY="" # Gemini API Key for parsing search query in "Find"
GEMINI_API_KEY="" # Gemini API Key for parsing search query in "Find"

# Immich Share Link
IMMICH_SHARE_LINK_KEY="" # Share link key for Immich
POWER_TOOLS_ENDPOINT_URL="" # URL of the Power Tools endpoint (Used for share links)
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ A unofficial immich client to provide better tools to organize and manage your i

[![Immich Power Tools](./screenshots/screenshot-1.png)](https://www.loom.com/embed/13aa90d8ab2e4acab0993bdc8703a750?sid=71498690-b745-473f-b239-a7bdbe6efc21)

### 🎒Features
- **Manage people data in bulk 👫**: Options to update people data in bulk, and with advance filters
- **People Merge Suggestion 🎭**: Option to bulk merge people with suggested faces based on similarity.
- **Update Missing Locations 📍**: Find assets in your library those are without location and update them with the location of the asset.
- **Potential Albums 🖼️**: Find albums that are potential to be created based on the assets and people in your library.
- **Analytics 📈**: Get analytics on your library like assets over time, exif data, etc.
- **Smart Search 🔎**: Search your library with natural language, supports queries like "show me all my photos from 2024 of <person name>"
- **Bulk Date Offset 📅**: Offset the date of selected assets by a given amount of time. Majorly used to fix the date of assets that are out of sync with the actual date.

### Support me 🙏

If you find this tool useful, please consider supporting me by buying me a coffee.

[![Buy me a coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/varunraj)

## 💭 Back story

Recently I've migrated my entire Google photos library to Immich, I was able to successfully migrate all my assets along with its albums to immich. But there were few things like people match that was lacking. I loved Immich UI on the whole but for organizing content I felt its quite restricted and I had to do a lot of things in bulk instead of opening each asset and doing it. Hence I built this tool (continuing to itereate) to make my life and any other Immich user's life easier.
Expand Down Expand Up @@ -51,7 +66,11 @@ Refer here for obtaining Immich API Key: https://immich.app/docs/features/comman
If you're using portainer, run the docker using `docker run` and add the power tools to the same network as immich.

```bash
# Run the power tools from docker
docker run -d --name immich_power_tools -p 8001:3000 --env-file .env ghcr.io/varun-raj/immich-power-tools:latest

# Add Power tools to the same network as immich
docker network connect immich_default immich_power_tools
```


Expand Down Expand Up @@ -90,7 +109,8 @@ bun run dev
- [x] Manage People
- [x] Smart Merge
- [x] Manage Albums
- [ ] Bulk Delete
- [x] Bulk Delete
- [x] Bulk Share
- [ ] Bulk Edit
- [ ] Filters
- [x] Potential Albums
Expand Down Expand Up @@ -127,7 +147,6 @@ Google Gemini 1.5 Flash model is used for parsing your search query in "Find" pa

> Code where Gemini is used: [src/helpers/gemini.helper.ts](./src/helpers/gemini.helper.ts)

## Contributing

Feel free to contribute to this project, I'm open to any suggestions and improvements. Please read the [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.
Binary file modified bun.lockb
Binary file not shown.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,15 @@
"cookie": "^0.6.0",
"date-fns": "^3.6.0",
"drizzle-orm": "^0.33.0",
"jsonwebtoken": "^9.0.2",
"leaflet": "^1.9.4",
"leaflet-defaulticon-compatibility": "0.1.2",
"lucide-react": "^0.428.0",
"next": "14.2.5",
"next-themes": "^0.3.0",
"pg": "^8.12.0",
"qs": "^6.13.0",
"rc-mentions": "^2.18.0",
"react": "^18",
"react-calendar-heatmap": "^1.9.0",
"react-day-picker": "9.0.8",
"react-dom": "^18",
"react-grid-gallery": "^1.0.1",
Expand Down
86 changes: 0 additions & 86 deletions src/components/albums/AlbumCreateDialog.tsx

This file was deleted.

79 changes: 72 additions & 7 deletions src/components/albums/AlbumSelectorDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import {
Dialog,
DialogContent,
Expand All @@ -9,18 +9,28 @@ import {
} from "../ui/dialog";
import { Button } from "../ui/button";
import { listAlbums } from "@/handlers/api/album.handler";
import { IAlbum } from "@/types/album";
import { IAlbum, IAlbumCreate } from "@/types/album";
import { Input } from "../ui/input";
import { usePotentialAlbumContext } from "@/contexts/PotentialAlbumContext";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
import toast from "react-hot-toast";
import { Label } from "../ui/label";

interface IProps {
onSelected: (album: IAlbum) => Promise<void>;
onCreated?: (album: IAlbum) => Promise<void>;
onSubmit?: (data: IAlbumCreate) => Promise<any>;
}
export default function AlbumSelectorDialog({ onSelected }: IProps) {
export default function AlbumSelectorDialog({ onSelected, onCreated, onSubmit }: IProps) {
const [open, setOpen] = useState(false);
const [albums, setAlbums] = useState<IAlbum[]>([]);
const [loading, setLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [search, setSearch] = useState("");
const { selectedIds, assets } = usePotentialAlbumContext();
const [formData, setFormData] = useState({
albumName: "",
});

const fetchData = () => {
return listAlbums()
Expand All @@ -42,11 +52,31 @@ export default function AlbumSelectorDialog({ onSelected }: IProps) {
});
}


const handleSubmit = useCallback((e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!onSubmit) return;
setLoading(true);
onSubmit(formData)
.then(() => {
toast.success("Album created successfully");
setFormData({ albumName: "" });
setOpen(false);
})
.catch((e) => {
toast.error("Failed to create album");
})
.finally(() => {
setLoading(false);
});
}, [onSubmit, formData]);


useEffect(() => {
if (open && !albums.length) fetchData();
}, [open]);

const renderContent = () => {
const renderContent = useCallback(() => {
if (loading) return <p>Loading...</p>;
if (errorMessage) return <p>{errorMessage}</p>;
return (
Expand All @@ -73,12 +103,36 @@ export default function AlbumSelectorDialog({ onSelected }: IProps) {
</div>
</div>
);
};
}, [albums, filteredAlbums, handleSelect, loading, errorMessage]);

const renderCreateContent = useCallback(() => {
return (
<form onSubmit={handleSubmit} className="flex flex-col gap-4 max-h-[500px] min-h-[500px]">
<p className="text-sm text-zinc-500 dark:text-zinc-400">
Create a new album and add the selected assets to it
</p>
<div className="flex flex-col gap-2">
<Label>Album Name</Label>
<Input
placeholder="Album Name"
onChange={(e) => {
setFormData({ ...formData, albumName: e.target.value });
}}
/>
</div>
<div className="self-end">
<Button disabled={loading} type="submit">
{loading ? "Creating Album" : "Create Album"}
</Button>
</div>
</form>
)
}, [loading, formData, handleSubmit]);

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button size={"sm"}>Select Album</Button>
<Button size={"sm"} disabled={!selectedIds.length}>Add to Album</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
Expand All @@ -87,7 +141,18 @@ export default function AlbumSelectorDialog({ onSelected }: IProps) {
Select the albums you want to add the selected assets to
</DialogDescription>
</DialogHeader>
{renderContent()}
<Tabs defaultValue="albums" className="w-full">
<TabsList className="w-full">
<TabsTrigger className="w-full" value="albums">Albums</TabsTrigger>
<TabsTrigger className="w-full" value="create">Create</TabsTrigger>
</TabsList>
<TabsContent value="albums">
{renderContent()}
</TabsContent>
<TabsContent value="create">
{renderCreateContent()}
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
);
Expand Down
Loading

0 comments on commit cd9e620

Please sign in to comment.