- press_release_images: Kundenangabe/Portalkennzeichnung getrennt, Herkunftsbefund, Beweisoriginal, Snapshot-Pfade, published_ever_at, rules_version, Fokus/Letterbox; Bestand unangetastet - legal_texts (insert-only) mit img-confirm-v1 (alter Wortlaut), v2 (AGB 0.6), v3 (AGB 0.7, noch ohne Datum); image-rights:backfill-confirmations - image_rights_confirmations polymorph, insert-only, IP als versionierter HMAC; Löschung nur für nie veröffentlichte Uploads - ImageProvenanceInspector (Imagick/exif): IPTC, XMP, EXIF; algorithmicallyEnhanced nur Vermerk; unlesbare Datei = error - PressReleaseImageUploader: Original vor Derivaten, Hash-Bindung, Titelbild-Regel unter Sperre, Dateien bei Fehlern zurückgerollt; Admin-Upload ohne Bestätigung - ImageService: kein Rückfall auf die Quelldatei, og-Variante, Fokuspunkt und no_crop Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
70 lines
2.3 KiB
PHP
70 lines
2.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Image;
|
|
|
|
use App\Enums\AiImageDeclaration;
|
|
use App\Enums\ImageProvenanceStatus;
|
|
|
|
/**
|
|
* Normalisiertes Ergebnis der Herkunftsprüfung (Bildkennzeichnung A.5).
|
|
* Wird als json am Bild und im Bestätigungs-Snapshot gespeichert.
|
|
*
|
|
* @phpstan-type IptcData array{digital_source_type: string|null, creator: list<string>, credit: string|null, byline: string|null, copyright_notice: string|null}
|
|
* @phpstan-type ExifData array{artist: string|null, copyright: string|null}
|
|
* @phpstan-type DerivedData array{ai_declaration_suggestion: string|null, credit_suggestion: string|null, credit_suggestion_source: string|null, credit_truncated: bool, enhancement_hint: bool}
|
|
*/
|
|
final readonly class ProvenanceResult
|
|
{
|
|
/**
|
|
* @param IptcData $iptc
|
|
* @param ExifData $exif
|
|
* @param DerivedData $derived
|
|
* @param array{class: string, message: string}|null $error
|
|
*/
|
|
public function __construct(
|
|
public ImageProvenanceStatus $status,
|
|
public string $fileSha256,
|
|
public array $iptc,
|
|
public array $exif,
|
|
public array $derived,
|
|
public string $toolVersion,
|
|
public int $durationMs,
|
|
public ?array $error = null,
|
|
) {}
|
|
|
|
public function suggestedAiDeclaration(): ?AiImageDeclaration
|
|
{
|
|
return AiImageDeclaration::tryFrom((string) $this->derived['ai_declaration_suggestion']);
|
|
}
|
|
|
|
public function creditSuggestion(): ?string
|
|
{
|
|
return $this->derived['credit_suggestion'];
|
|
}
|
|
|
|
/**
|
|
* Nur ein vertrauenswürdig geprüfter Befund darf die Kennzeichnung
|
|
* erzwingen (Decision-Update E9). In Phase 1 immer null.
|
|
*/
|
|
public function enforcedAiDeclaration(): ?AiImageDeclaration
|
|
{
|
|
return $this->status->allowsLock() ? $this->suggestedAiDeclaration() : null;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return array_filter([
|
|
'status' => $this->status->value,
|
|
'file_sha256' => $this->fileSha256,
|
|
'tool' => $this->toolVersion,
|
|
'duration_ms' => $this->durationMs,
|
|
'iptc' => $this->iptc,
|
|
'exif' => $this->exif,
|
|
'derived' => $this->derived,
|
|
'error' => $this->error,
|
|
], fn ($value) => $value !== null);
|
|
}
|
|
}
|