diff --git a/app/jobs/enqueue_background_job.rb b/app/jobs/enqueue_background_job.rb index aa5cdccf..61e103c3 100644 --- a/app/jobs/enqueue_background_job.rb +++ b/app/jobs/enqueue_background_job.rb @@ -7,6 +7,8 @@ class EnqueueBackgroundJob < ApplicationJob case job_name when 'start_immich_import' Import::ImmichGeodataJob.perform_later(user_id) + when 'start_photoprism_import' + Import::PhotoprismGeodataJob.perform_later(user_id) when 'start_reverse_geocoding', 'continue_reverse_geocoding' Jobs::Create.new(job_name, user_id).call else diff --git a/app/jobs/import/photoprism_geodata_job.rb b/app/jobs/import/photoprism_geodata_job.rb new file mode 100644 index 00000000..7aa2d27e --- /dev/null +++ b/app/jobs/import/photoprism_geodata_job.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class Import::PhotoprismGeodataJob < ApplicationJob + queue_as :imports + sidekiq_options retry: false + + def perform(user_id) + user = User.find(user_id) + + Photoprism::ImportGeodata.new(user).call + end +end diff --git a/app/models/import.rb b/app/models/import.rb index c6e5a8a6..067baf12 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -10,7 +10,7 @@ class Import < ApplicationRecord enum :source, { google_semantic_history: 0, owntracks: 1, google_records: 2, - google_phone_takeout: 3, gpx: 4, immich_api: 5, geojson: 6 + google_phone_takeout: 3, gpx: 4, immich_api: 5, geojson: 6, photoprism_api: 7 } def process! diff --git a/app/services/immich/import_geodata.rb b/app/services/immich/import_geodata.rb index 766643a7..469761d6 100644 --- a/app/services/immich/import_geodata.rb +++ b/app/services/immich/import_geodata.rb @@ -57,7 +57,7 @@ class Immich::ImportGeodata end def log_no_data - Rails.logger.info 'No data found' + Rails.logger.info 'No geodata found for Immich' end def create_import_failed_notification(import_name) diff --git a/app/services/photoprism/import_geodata.rb b/app/services/photoprism/import_geodata.rb new file mode 100644 index 00000000..a24f2542 --- /dev/null +++ b/app/services/photoprism/import_geodata.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +class Photoprism::ImportGeodata + attr_reader :user, :start_date, :end_date + + def initialize(user, start_date: '1970-01-01', end_date: nil) + @user = user + @start_date = start_date + @end_date = end_date + end + + def call + photoprism_data = retrieve_photoprism_data + + log_no_data and return if photoprism_data.empty? + + photoprism_data_json = parse_photoprism_data(photoprism_data) + file_name = file_name(photoprism_data_json) + import = user.imports.find_or_initialize_by(name: file_name, source: :photoprism_api) + + create_import_failed_notification(import.name) and return unless import.new_record? + + import.raw_data = photoprism_data_json + import.save! + + ImportJob.perform_later(user.id, import.id) + end + + private + + def retrieve_photoprism_data + Photoprism::RequestPhotos.new(user, start_date:, end_date:).call + end + + def parse_photoprism_data(photoprism_data) + geodata = photoprism_data.map do |asset| + next unless valid?(asset) + + extract_geodata(asset) + end + + geodata.compact.sort_by { |data| data[:timestamp] } + end + + def valid?(asset) + asset['Lat'] && + asset['Lat'] != 0 && + asset['Lng'] && + asset['Lng'] != 0 && + asset['TakenAt'] + end + + def extract_geodata(asset) + { + latitude: asset.dig('exifInfo', 'latitude'), + longitude: asset.dig('exifInfo', 'longitude'), + timestamp: Time.zone.parse(asset.dig('exifInfo', 'dateTimeOriginal')).to_i + } + end + + def log_no_data + Rails.logger.info 'No geodata found for Photoprism' + end + + def create_import_failed_notification(import_name) + Notifications::Create.new( + user:, + kind: :info, + title: 'Import was not created', + content: "Import with the same name (#{import_name}) already exists. If you want to proceed, delete the existing import and try again." + ).call + end + + def file_name(photoprism_data_json) + from = Time.zone.at(photoprism_data_json.first[:timestamp]).to_date + to = Time.zone.at(photoprism_data_json.last[:timestamp]).to_date + + "photoprism-geodata-#{user.email}-from-#{from}-to-#{to}.json" + end +end diff --git a/app/views/imports/index.html.erb b/app/views/imports/index.html.erb index 32ab69ff..b3f8cbfc 100644 --- a/app/views/imports/index.html.erb +++ b/app/views/imports/index.html.erb @@ -10,6 +10,11 @@ <% else %> Import Immich data <% end %> + <% if current_user.settings['photoprism_url'] && current_user.settings['photoprism_api_key'] %> + <%= link_to 'Import Photoprism data', settings_background_jobs_path(job_name: 'start_photoprism_import'), method: :post, data: { confirm: 'Are you sure?', turbo_confirm: 'Are you sure?', turbo_method: :post }, class: 'rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium' %> + <% else %> + Import Photoprism data + <% end %>