diff --git a/app/Actions/Photo/Pipes/Shared/HydrateMetadata.php b/app/Actions/Photo/Pipes/Shared/HydrateMetadata.php index e469147a991..1baeac006e4 100644 --- a/app/Actions/Photo/Pipes/Shared/HydrateMetadata.php +++ b/app/Actions/Photo/Pipes/Shared/HydrateMetadata.php @@ -66,6 +66,7 @@ public function handle(DuplicateDTO|StandaloneDTO $state, \Closure $next): Dupli } if ($state->photo->taken_at === null) { $state->photo->taken_at = $state->exifInfo->taken_at; + $state->photo->initial_taken_at = $state->exifInfo->taken_at; } if ($state->photo->latitude === null) { $state->photo->latitude = $state->exifInfo->latitude; diff --git a/app/Contracts/Http/Requests/HasTakenAt.php b/app/Contracts/Http/Requests/HasTakenAt.php new file mode 100644 index 00000000000..e5751dc9260 --- /dev/null +++ b/app/Contracts/Http/Requests/HasTakenAt.php @@ -0,0 +1,19 @@ +created_at = $request->uploadDate(); $photo->tags = $request->tags(); $photo->license = $request->license()->value; + + // if the request takenAt is null, then we set the initial value back. + $photo->taken_at = $request->takenAt() ?? $photo->initial_taken_at; + $photo->save(); return PhotoResource::fromModel($photo); diff --git a/app/Http/Requests/Photo/EditPhotoRequest.php b/app/Http/Requests/Photo/EditPhotoRequest.php index 3a95390aa05..5748243bcbf 100644 --- a/app/Http/Requests/Photo/EditPhotoRequest.php +++ b/app/Http/Requests/Photo/EditPhotoRequest.php @@ -12,6 +12,7 @@ use App\Contracts\Http\Requests\HasLicense; use App\Contracts\Http\Requests\HasPhoto; use App\Contracts\Http\Requests\HasTags; +use App\Contracts\Http\Requests\HasTakenAt; use App\Contracts\Http\Requests\HasTitle; use App\Contracts\Http\Requests\HasUploadDate; use App\Contracts\Http\Requests\RequestAttribute; @@ -21,6 +22,7 @@ use App\Http\Requests\Traits\HasLicenseTrait; use App\Http\Requests\Traits\HasPhotoTrait; use App\Http\Requests\Traits\HasTagsTrait; +use App\Http\Requests\Traits\HasTakenAtDateTrait; use App\Http\Requests\Traits\HasTitleTrait; use App\Http\Requests\Traits\HasUploadDateTrait; use App\Models\Photo; @@ -32,7 +34,7 @@ use Illuminate\Support\Facades\Gate; use Illuminate\Validation\Rules\Enum; -class EditPhotoRequest extends BaseApiRequest implements HasPhoto, HasTags, HasUploadDate, HasDescription, HasLicense, HasTitle +class EditPhotoRequest extends BaseApiRequest implements HasPhoto, HasTags, HasUploadDate, HasDescription, HasLicense, HasTitle, HasTakenAt { use HasPhotoTrait; use HasTitleTrait; @@ -40,6 +42,7 @@ class EditPhotoRequest extends BaseApiRequest implements HasPhoto, HasTags, HasU use HasTagsTrait; use HasUploadDateTrait; use HasLicenseTrait; + use HasTakenAtDateTrait; /** * {@inheritDoc} @@ -62,6 +65,7 @@ public function rules(): array RequestAttribute::TAGS_ATTRIBUTE . '.*' => ['required', 'string', 'min:1'], RequestAttribute::LICENSE_ATTRIBUTE => ['required', new Enum(LicenseType::class)], RequestAttribute::UPLOAD_DATE_ATTRIBUTE => ['required', 'date'], + RequestAttribute::TAKEN_DATE_ATTRIBUTE => ['nullable', 'date'], ]; } @@ -82,5 +86,10 @@ protected function processValidatedValues(array $values, array $files): void $this->tags = $values[RequestAttribute::TAGS_ATTRIBUTE]; $this->upload_date = Carbon::parse($values[RequestAttribute::UPLOAD_DATE_ATTRIBUTE]); $this->license = LicenseType::tryFrom($values[RequestAttribute::LICENSE_ATTRIBUTE]); + + // We only set this one if it is not null + if (isset($values[RequestAttribute::TAKEN_DATE_ATTRIBUTE])) { + $this->taken_at = Carbon::parse($values[RequestAttribute::TAKEN_DATE_ATTRIBUTE]); + } } } diff --git a/app/Http/Requests/Traits/HasTakenAtDateTrait.php b/app/Http/Requests/Traits/HasTakenAtDateTrait.php new file mode 100644 index 00000000000..d4da4524db3 --- /dev/null +++ b/app/Http/Requests/Traits/HasTakenAtDateTrait.php @@ -0,0 +1,24 @@ +taken_at; + } +} \ No newline at end of file diff --git a/app/Http/Resources/Models/Utils/PreComputedPhotoData.php b/app/Http/Resources/Models/Utils/PreComputedPhotoData.php index 78a8086048f..14fdfddb3c4 100644 --- a/app/Http/Resources/Models/Utils/PreComputedPhotoData.php +++ b/app/Http/Resources/Models/Utils/PreComputedPhotoData.php @@ -21,6 +21,7 @@ class PreComputedPhotoData extends Data public bool $is_camera_date; public bool $has_exif; public bool $has_location; + public bool $is_taken_at_modified; public function __construct(Photo $photo) { @@ -30,6 +31,10 @@ public function __construct(Photo $photo) $this->is_camera_date = $photo->taken_at !== null; $this->has_exif = $this->genExifHash($photo) !== ''; $this->has_location = $this->has_location($photo); + // if taken_at is null, it is for sure not modified. + // if taken_at is not null, then it is modified if initial_taken_at is null or if taken_at is different from initial_taken_at. + // dd($photo->taken_at, $photo->initial_taken_at, $photo->taken_at->notEqualTo($photo->initial_taken_at)); + $this->is_taken_at_modified = $photo->taken_at !== null && ($photo->initial_taken_at === null || $photo->taken_at->notEqualTo($photo->initial_taken_at)); } private function has_location(Photo $photo): bool diff --git a/app/Legacy/Actions/Photo/Strategies/AbstractAddStrategy.php b/app/Legacy/Actions/Photo/Strategies/AbstractAddStrategy.php index eff088adb83..ee16034c567 100644 --- a/app/Legacy/Actions/Photo/Strategies/AbstractAddStrategy.php +++ b/app/Legacy/Actions/Photo/Strategies/AbstractAddStrategy.php @@ -82,6 +82,7 @@ protected function hydrateMetadata(): void } if ($this->photo->taken_at === null) { $this->photo->taken_at = $this->parameters->exifInfo->taken_at; + $this->photo->initial_taken_at = $this->parameters->exifInfo->taken_at; } if ($this->photo->latitude === null) { $this->photo->latitude = $this->parameters->exifInfo->latitude; diff --git a/app/Models/Photo.php b/app/Models/Photo.php index 2e4bb9aeec9..100419d6f1d 100644 --- a/app/Models/Photo.php +++ b/app/Models/Photo.php @@ -61,6 +61,8 @@ * @property string|null $location * @property Carbon|null $taken_at * @property string|null $taken_at_orig_tz + * @property Carbon|null $initial_taken_at + * @property string|null $initial_taken_at_orig_tz * @property bool $is_starred * @property string|null $live_photo_short_path * @property string|null $live_photo_url @@ -153,7 +155,9 @@ class Photo extends Model 'created_at' => 'datetime', 'updated_at' => 'datetime', 'taken_at' => DateTimeWithTimezoneCast::class, + 'initial_taken_at' => DateTimeWithTimezoneCast::class, 'live_photo_url' => MustNotSetCast::class . ':live_photo_short_path', + 'taken_at_mod' => 'datetime', 'owner_id' => 'integer', 'is_starred' => 'boolean', 'tags' => ArrayCast::class, diff --git a/database/factories/PhotoFactory.php b/database/factories/PhotoFactory.php index 71be09427ef..689efef5758 100644 --- a/database/factories/PhotoFactory.php +++ b/database/factories/PhotoFactory.php @@ -37,6 +37,8 @@ class PhotoFactory extends Factory */ public function definition(): array { + $now = now(); + return [ 'title' => 'CR_' . fake()->numerify('####'), 'description' => null, @@ -50,8 +52,10 @@ public function definition(): array 'lens' => 'EF200mm f/2L IS', 'shutter' => '1/320 s', 'focal' => '200mm', - 'taken_at' => now(), + 'initial_taken_at' => $now, 'taken_at_orig_tz' => null, + 'taken_at' => $now, + 'initial_taken_at_orig_tz' => null, 'is_starred' => false, 'album_id' => null, 'checksum' => sha1(rand()), diff --git a/database/migrations/2025_01_24_200235_add_initial_taken_at.php b/database/migrations/2025_01_24_200235_add_initial_taken_at.php new file mode 100644 index 00000000000..0e13d288dfd --- /dev/null +++ b/database/migrations/2025_01_24_200235_add_initial_taken_at.php @@ -0,0 +1,43 @@ +dateTime('initial_taken_at', 0)->nullable(true)->default(null)->after('taken_at_orig_tz')->comment('backup of the original taken_at value'); + $table->string('initial_taken_at_orig_tz', 31)->nullable(true)->default(null)->after('initial_taken_at')->comment('backup of the timezone at which the photo has originally been taken'); + }); + + // Set initial values + DB::table('photos')->update([ + 'initial_taken_at' => DB::raw('taken_at'), + 'initial_taken_at_orig_tz' => DB::raw('taken_at_orig_tz'), + ]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('photos', function (Blueprint $table) { + $table->dropColumn('initial_taken_at_orig_tz'); + }); + Schema::table('photos', function (Blueprint $table) { + $table->dropColumn('initial_taken_at'); + }); + } +}; diff --git a/lang/cz/gallery.php b/lang/cz/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/cz/gallery.php +++ b/lang/cz/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/de/gallery.php b/lang/de/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/de/gallery.php +++ b/lang/de/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/el/gallery.php b/lang/el/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/el/gallery.php +++ b/lang/el/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/en/gallery.php b/lang/en/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/en/gallery.php +++ b/lang/en/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/es/gallery.php b/lang/es/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/es/gallery.php +++ b/lang/es/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/fr/gallery.php b/lang/fr/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/fr/gallery.php +++ b/lang/fr/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/hu/gallery.php b/lang/hu/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/hu/gallery.php +++ b/lang/hu/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/it/gallery.php b/lang/it/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/it/gallery.php +++ b/lang/it/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/ja/gallery.php b/lang/ja/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/ja/gallery.php +++ b/lang/ja/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/nl/gallery.php b/lang/nl/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/nl/gallery.php +++ b/lang/nl/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/no/gallery.php b/lang/no/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/no/gallery.php +++ b/lang/no/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/pl/gallery.php b/lang/pl/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/pl/gallery.php +++ b/lang/pl/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/pt/gallery.php b/lang/pt/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/pt/gallery.php +++ b/lang/pt/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/ru/gallery.php b/lang/ru/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/ru/gallery.php +++ b/lang/ru/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/sk/gallery.php b/lang/sk/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/sk/gallery.php +++ b/lang/sk/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/sv/gallery.php b/lang/sv/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/sv/gallery.php +++ b/lang/sv/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/vi/gallery.php b/lang/vi/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/vi/gallery.php +++ b/lang/vi/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/zh_CN/gallery.php b/lang/zh_CN/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/zh_CN/gallery.php +++ b/lang/zh_CN/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/lang/zh_TW/gallery.php b/lang/zh_TW/gallery.php index eb8008827e0..a56564245f2 100644 --- a/lang/zh_TW/gallery.php +++ b/lang/zh_TW/gallery.php @@ -177,6 +177,8 @@ 'no_tags' => 'No Tags', 'set_tags' => 'Set Tags', 'set_created_at' => 'Set Upload Date', + 'set_taken_at' => 'Set Taken Date', + 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.', ], ], diff --git a/resources/js/components/drawers/PhotoDetails.vue b/resources/js/components/drawers/PhotoDetails.vue index 1b4f1135127..283e40b45da 100644 --- a/resources/js/components/drawers/PhotoDetails.vue +++ b/resources/js/components/drawers/PhotoDetails.vue @@ -2,9 +2,9 @@