Smart Albums are virtual, dynamically-generated albums in Lychee that automatically contain photos based on specific criteria. Unlike regular albums where photos are explicitly assigned, Smart Albums collect photos that match certain conditions like being highlighted, recently uploaded, or unassigned to any album.
Smart Albums implement the AbstractAlbum interface but extend BaseSmartAlbum instead of being traditional Eloquent models. They are singleton classes that cannot be created or deleted by users - they always exist when enabled in the configuration.
- Virtual Albums: No database storage for the album itself, only for photos
- Dynamic Content: Photos appear/disappear based on their properties
- Singleton Pattern: One instance per smart album type
- Read-Only: Cannot be created, deleted, or have photos manually added/removed
- Configurable: Can be enabled/disabled via configuration settings
The BaseSmartAlbum class provides the common functionality for all smart albums:
- Smart Photo Condition: A closure that defines the filtering criteria
- Photo Query Policy: Applies security and visibility filters
- Caching: Results are cached for performance
- Thumbnail Generation: Automatic thumbnail selection based on configuration
// Constructor accepts the smart album type and filtering condition
protected function __construct(SmartAlbumType $id, \Closure $smart_condition)
// Returns photos matching the smart condition with security filters applied
public function photos(): Builder
// Cached photo retrieval with default sorting
protected function getPhotosAttribute(): LengthAwarePaginator
// Thumbnail generation with random or sorted selection
protected function getThumbAttribute(): ?ThumbSmart Albums respect Lychee's security model through:
- PhotoQueryPolicy: Filters photos based on user permissions
- Searchability Filter: Ensures users only see photos they have access to
- Sensitivity Filtering: Respects the
hide_nsfw_in_smart_albumsconfiguration - Override Setting:
SA_override_visibilitycan bypass some security filters
Purpose: Contains recently uploaded photos based on their creation date.
Filtering Logic:
$query->where('photos.created_at', '>=', $recent_cutoff_date);Configuration:
enable_recent: Enable/disable the Recent albumrecent_age: Number of days to consider "recent" (default varies)
Behavior: Shows photos uploaded within the configured number of days from the current date.
Purpose: Contains all photos marked as highlighted by users.
Filtering Logic:
$query->where('photos.is_highlighted', '=', true);Configuration:
enable_highlighted: Enable/disable the Highlighted album
Behavior: Shows all photos with the is_highlighted flag set to true, regardless of their album assignment.
Purpose: Contains photos taken on the same month and day in previous years.
Filtering Logic:
$query->where(fn (Builder $q) => $q
->whereMonth('photos.taken_at', '=', $today->month)
->whereDay('photos.taken_at', '=', $today->day))
->orWhere(fn (Builder $q) => $q
->whereNull('photos.taken_at')
->whereYear('photos.created_at', '<', $today->year)
->whereMonth('photos.created_at', '=', $today->month)
->whereDay('photos.created_at', '=', $today->day));Configuration:
enable_on_this_day: Enable/disable the On This Day album
Behavior:
- Primary: Shows photos taken on this day in previous years (uses
taken_atEXIF data) - Fallback: For photos without EXIF date, uses upload date (
created_at) from previous years
Purpose: Contains photos that are not assigned to any regular album.
Filtering Logic:
$query->whereNull(PA::ALBUM_ID); // Where album_id is null in the photo_album relationshipConfiguration:
enable_unsorted: Enable/disable the Unsorted album
Special Behavior - The Edge Case:
The UnsortedAlbum has unique authorization logic that differs from other smart albums:
public function photos(): Builder
{
if ($this->public_permissions !== null) {
// If Unsorted album is made public, ALL unsorted photos become visible
// This bypasses normal user ownership checks
return Photo::query()
->leftJoin(PA::PHOTO_ALBUM, 'photos.id', '=', PA::PHOTO_ID)
->with(['size_variants', 'statistics', 'palette'])
->where($this->smart_photo_condition);
}
// Normal security filtering applies
return parent::photos();
}Why It's an Edge Case:
- Ownership Bypass: When the Unsorted album is made public, it shows ALL unsorted photos from ALL users, not just the current user's photos
- Security Implications: This can expose photos that users might not intend to be public
- Different Authorization: Unlike other smart albums that always respect photo ownership, Unsorted can override this when public
- Administrative Feature: Primarily intended for admin users to see all unorganized content in the system
Purpose: Contains photos that are not assigned to any tag.
Filtering Logic:
$query->whereDoesntHave('tags');Configuration:
enable_untagged: Enable/disable the Untagged album
Behavior: Shows all photos that do not have any tags associated with them.
Smart Albums can be individually enabled/disabled:
// In Configs table
'enable_unsorted' => true/false
'enable_highlighted' => true/false
'enable_recent' => true/false
'enable_on_this_day' => true/false
'enable_untagged' => true/false
// Additional settings
'recent_age' => 30 (days)
'SA_override_visibility' => false
'hide_nsfw_in_smart_albums' => true
'SA_random_thumbs' => falseEach smart album uses the singleton pattern to ensure only one instance exists:
private ?self $instance = null;
public function getInstance(): self
{
return self::$instance ??= new self();
}Smart Albums use the MimicModel trait to behave like Eloquent models while not being stored in the database:
- Provides
__get()method for dynamic property access - Enables consistent API with regular albums
- Allows smart albums to be treated uniformly in the frontend
- Query Caching: Photo results are cached after first retrieval
- Lazy Loading: Photos are only queried when actually accessed
- Optimized Queries: Use joins and indexes for efficient filtering
- Thumbnail Caching: Thumbnails are cached and can be randomized
Smart album titles are automatically translated using Laravel's localization system:
$this->title = __('gallery.smart_album.' . strtolower($id->name)) ?? $id->name;Translation keys in lang/{locale}/gallery.php:
gallery.smart_album.unsortedgallery.smart_album.highlightedgallery.smart_album.recentgallery.smart_album.on_this_daygallery.smart_album.untagged
Smart Albums appear alongside regular albums in the main gallery view when enabled. They:
- Are fetched by the
Albums/Top.phpAction - Appear in the frontend with special smart album styling
- Support the same photo operations (mostly) as regular albums
- Respect user permissions and privacy settings
- Can have their own access permissions via the
AccessPermissionmodel
The smart album system provides users with convenient ways to access their photos based on common organizational patterns without manual curation.
Last updated: August 14, 2025