Skip to content

Instantly share code, notes, and snippets.

@tsvetkovv
Last active December 31, 2024 18:50
Show Gist options
  • Save tsvetkovv/114182a59582c0c680fd64f0cd059758 to your computer and use it in GitHub Desktop.
Save tsvetkovv/114182a59582c0c680fd64f0cd059758 to your computer and use it in GitHub Desktop.
Create Immich albums from the folder structure
/**
* Immich Album Creator Script
*
* This script automatically creates albums in Immich based on your folder structure.
*
* Expected folder structure:
* mnt/originals/{year}/{month}/{album-name}
*
* Examples of valid paths:
* - mnt/originals/2023/05/2023-05-01-Birthday
* - mnt/originals/2016/04/2016-04-10-Italy
*
* The script will:
* 1. Only process folders that follow the pattern above
* 2. Create an album using the last part of the path as the album name
* 3. Add all photos from that folder to the created album
*
* To modify the folder structure detection:
* 1. Find the hasAlbumName() function
* 2. Modify the conditions to match your structure
*
* Example for different folder structure:
* For structure like "photos/2023/Family/Greece":
*
* const hasAlbumName = (path) => {
* const parts = path.split('/');
* return parts.length > 3 && parts[0] === 'photos';
* };
*
* To modify the album naming:
* 1. Find the getAlbumName() function
* 2. Adjust the logic to extract the desired album name
*
* Known limitations:
* - Must be run while logged into Immich
* - Processes all matching folders at once
*
* Usage:
* 1. Login into Immich web page and open browser developer tools (F12)
* 2. Copy and paste this entire script
* 3. Press Enter to execute
*/
async function createImmichAlbums() {
// Helper function to delay between requests
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
// Helper function to extract album name from path
const getAlbumName = (path) => {
const parts = path.split('/');
return parts[parts.length - 1];
};
// Helper function to check if path contains an album name
const hasAlbumName = (path) => {
const parts = path.split('/');
// Check both mnt/originals/... and mnt/... patterns
if (parts[0] !== 'mnt') return false;
if (parts[1] === 'originals') {
return parts.length > 4;
}
return parts.length > 3;
};
try {
// 1. Fetch existing albums first
const existingAlbumsResponse = await fetch('/api/albums');
if (!existingAlbumsResponse.ok) throw new Error('Failed to fetch existing albums');
const existingAlbums = await existingAlbumsResponse.json();
const existingAlbumNames = new Set(existingAlbums.map(album => album.albumName));
console.log(`Found ${existingAlbumNames.size} existing albums`);
// 2. Fetch all unique paths
const pathsResponse = await fetch('/api/view/folder/unique-paths');
if (!pathsResponse.ok) throw new Error('Failed to fetch paths');
const paths = await pathsResponse.json();
// 3. Filter paths that contain album names
const albumPaths = paths.filter(hasAlbumName);
console.log(`Found ${albumPaths.length} potential albums to process`);
// 4. Process each album path
for (const path of albumPaths) {
try {
const albumName = getAlbumName(path);
// Skip if album already exists
if (existingAlbumNames.has(albumName)) {
console.log(`Skipping "${albumName}" - album already exists`);
continue;
}
// Get photos for this folder
const folderResponse = await fetch(`/api/view/folder?path=${encodeURIComponent(path)}`);
if (!folderResponse.ok) throw new Error(`Failed to fetch folder contents for ${path}`);
const folderContents = await folderResponse.json();
const photoIds = folderContents.map(photo => photo.id);
if (photoIds.length === 0) {
console.log(`Skipping ${path} - no photos found`);
continue;
}
// Create album
const createAlbumResponse = await fetch('/api/albums', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ albumName })
});
if (!createAlbumResponse.ok) throw new Error(`Failed to create album ${albumName}`);
const album = await createAlbumResponse.json();
// Add photos to album
const addPhotosResponse = await fetch(`/api/albums/${album.id}/assets`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ ids: photoIds })
});
if (!addPhotosResponse.ok) throw new Error(`Failed to add photos to album ${albumName}`);
console.log(`Successfully created album "${albumName}" with ${photoIds.length} photos`);
// Add a small delay between operations to avoid overwhelming the server
await delay(1000);
} catch (error) {
console.error(`Error processing path ${path}:`, error);
continue; // Continue with next album even if this one fails
}
}
console.log('Album creation process completed!');
} catch (error) {
console.error('Failed to process albums:', error);
}
}
createImmichAlbums();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment