diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index f823a8e7..08196e09 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -64,4 +64,11 @@ justify-content: center; background: transparent; border: none; + border-radius: 50%; +} + +.photo-marker img { + border-radius: 50%; + width: 48px; + height: 48px; } diff --git a/app/controllers/api/v1/photos_controller.rb b/app/controllers/api/v1/photos_controller.rb index fba900c2..93f17208 100644 --- a/app/controllers/api/v1/photos_controller.rb +++ b/app/controllers/api/v1/photos_controller.rb @@ -4,8 +4,32 @@ class Api::V1::PhotosController < ApiController def index @photos = Rails.cache.fetch("photos_#{params[:start_date]}_#{params[:end_date]}", expires_in: 1.day) do Immich::RequestPhotos.new(current_api_user, start_date: params[:start_date], end_date: params[:end_date]).call - end + end.reject { |photo| photo['type'].downcase == 'video' } render json: @photos, status: :ok end + + def thumbnail + response = Rails.cache.fetch("photo_thumbnail_#{params[:id]}", expires_in: 1.day) do + HTTParty.get( + "#{current_api_user.settings['immich_url']}/api/assets/#{params[:id]}/thumbnail?size=preview", + headers: { + 'x-api-key' => current_api_user.settings['immich_api_key'], + 'accept' => 'application/octet-stream' + } + ) + end + + if response.success? + send_data( + response.body, + type: 'image/jpeg', + disposition: 'inline', + status: :ok + ) + else + Rails.logger.error "Failed to fetch thumbnail: #{response.code} - #{response.body}" + render json: { error: 'Failed to fetch thumbnail' }, status: response.code + end + end end diff --git a/app/javascript/controllers/maps_controller.js b/app/javascript/controllers/maps_controller.js index 4153624d..8a54ea99 100644 --- a/app/javascript/controllers/maps_controller.js +++ b/app/javascript/controllers/maps_controller.js @@ -782,7 +782,10 @@ export default class extends Controller { this.layerControl = L.control.layers(this.baseMaps(), layerControl).addTo(this.map); } - async fetchAndDisplayPhotos(startDate, endDate) { + async fetchAndDisplayPhotos(startDate, endDate, retryCount = 0) { + const MAX_RETRIES = 3; + const RETRY_DELAY = 3000; // 3 seconds + try { const params = new URLSearchParams({ api_key: this.apiKey, @@ -796,46 +799,57 @@ export default class extends Controller { } const photos = await response.json(); - - // Clear existing photo markers this.photoMarkers.clearLayers(); - // Create markers for each photo with coordinates - photos.forEach(photo => { - if (photo.exifInfo?.latitude && photo.exifInfo?.longitude) { - const marker = L.marker([photo.exifInfo.latitude, photo.exifInfo.longitude], { - icon: L.divIcon({ - className: 'photo-marker', - html: `
Taken: ${new Date(photo.localDateTime).toLocaleString()}
-Location: ${photo.exifInfo.city}, ${photo.exifInfo.state}, ${photo.exifInfo.country}
- ${photo.type === 'VIDEO' ? '🎥 Video' : '📷 Photo'} -Taken: ${new Date(photo.localDateTime).toLocaleString()}
+Location: ${photo.exifInfo.city}, ${photo.exifInfo.state}, ${photo.exifInfo.country}
+ ${photo.type === 'VIDEO' ? '🎥 Video' : '📷 Photo'} +