Look at digitalSourceType C2PA info

This commit is contained in:
N-Pex 2025-07-11 14:36:58 +02:00
parent b7875944f1
commit 6b56fe61fa
9 changed files with 189 additions and 82 deletions

View file

@ -2,15 +2,30 @@
<div v-if="c2pa">
<div class="detail-title">
{{ t("file_mode.content_credentials") }}
<v-icon>$vuetify.icons.ic_cr</v-icon>
</div>
<div class="detail-subtitle">
{{ t("file_mode.content_credentials_info") }} <a href="https://proofmode.org/verify" target="_blank">{{ t("file_mode.learn_more") }}</a>
{{ t("file_mode.content_credentials_info") }}
<!-- <a href="" target="_blank">{{ t("file_mode.learn_more") }}</a> -->
</div>
<div class="detail-row" v-if="dateCreated"><v-icon>$vuetify.icons.ic_exif_time</v-icon>{{ dateCreated }}</div>
<div class="detail-row" v-if="creator"><v-icon>$vuetify.icons.ic_exif_device_camera</v-icon>{{ creator }}</div>
<div class="detail-row" v-if="aiInferenceResult?.aiGenerated">
<div class="detail-row" v-if="screenCapture">
<v-icon>$vuetify.icons.ic_media_screenshot</v-icon>{{ screenCapture }}
</div>
<template v-else>
<div class="detail-row" v-if="dateCreated">
<v-icon>$vuetify.icons.ic_exif_time</v-icon>{{ dateCreatedDisplay }}
</div>
<div class="detail-row" v-if="creator"><v-icon>$vuetify.icons.ic_media_device</v-icon>{{ creator }}</div>
<div class="detail-row" v-if="valid && cameraCapture">
<v-icon>$vuetify.icons.ic_media_camera</v-icon>{{ cameraCapture }}
</div>
</template>
<div class="detail-row" v-if="ai || aiInferenceResult?.aiGenerated">
<v-icon>$vuetify.icons.ic_cc_ai</v-icon>{{ t("file_mode.ai_used") }}
</div>
<div class="detail-row" v-if="showAgeWarning">
<v-icon>$vuetify.icons.ic_media_flag</v-icon>{{ t("file_mode.old_photo") }}
</div>
<!-- {{ JSON.stringify(props.c2pa, undefined, 4) }} -->
</div>
@ -18,9 +33,19 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { AIInferenceResult, C2PAActionsAssertion, C2PAData } from "../../models/proof";
import { computed } from "vue";
import dayjs from "dayjs";
import {
AIInferenceResult,
C2PAActionsAssertion,
C2PAData,
C2PASourceTypeCompositeCapture,
C2PASourceTypeCompositeWithTrainedAlgorithmicMedia,
C2PASourceTypeComputationalCapture,
C2PASourceTypeDigitalCapture,
C2PASourceTypeScreenCapture,
C2PASourceTypeTrainedAlgorithmicMedia,
} from "../../models/proof";
import { computed, ref, Ref, watch } from "vue";
import dayjs, { Dayjs } from "dayjs";
const { t } = useI18n();
@ -29,45 +54,78 @@ const props = defineProps<{
aiInferenceResult?: AIInferenceResult;
}>();
const dateCreated = computed(() => {
try {
const manifests = Object.values(props.c2pa?.manifest_info.manifests ?? {});
for (const manifest of manifests) {
const createAssertion = manifest.assertions.find((a) => {
if (a.label === "c2pa.actions") {
const actions = (a.data as C2PAActionsAssertion)?.actions ?? [];
if (actions.find((a) => a.action === "c2pa.created")) {
return true;
//console.error("C2PA", JSON.stringify(props.c2pa, undefined, 4));
const creator: Ref<string | undefined> = ref(undefined);
const dateCreated: Ref<Dayjs | undefined> = ref(undefined);
const screenCapture: Ref<string | undefined> = ref(undefined);
const cameraCapture: Ref<string | undefined> = ref(undefined);
const ai: Ref<boolean> = ref(false);
const valid: Ref<boolean> = ref(false);
watch(
props,
() => {
creator.value = undefined;
dateCreated.value = undefined;
screenCapture.value = undefined;
cameraCapture.value = undefined;
ai.value = false;
valid.value = false;
try {
const manifests = Object.values(props.c2pa?.manifest_info.manifests ?? {});
for (const manifest of manifests) {
for (const assertion of manifest.assertions) {
if (assertion.label === "c2pa.actions") {
const actions = (assertion.data as C2PAActionsAssertion)?.actions ?? [];
const a = actions.find((a) => a.action === "c2pa.created");
if (a) {
creator.value = a.softwareAgent;
dateCreated.value = dayjs(Date.parse(manifest.signature_info.time));
if (a.digitalSourceType === C2PASourceTypeScreenCapture) {
screenCapture.value = t("file_mode.screenshot_taken_on", { date: dateCreated.value });
}
if (
a.digitalSourceType === C2PASourceTypeDigitalCapture ||
a.digitalSourceType === C2PASourceTypeComputationalCapture ||
a.digitalSourceType === C2PASourceTypeCompositeCapture
) {
cameraCapture.value = t("file_mode.captured_with_camera");
}
if (
a.digitalSourceType === C2PASourceTypeTrainedAlgorithmicMedia ||
a.digitalSourceType === C2PASourceTypeCompositeWithTrainedAlgorithmicMedia
) {
ai.value = true;
}
return;
}
}
}
return false;
});
if (createAssertion) {
const d = dayjs(Date.parse(manifest.signature_info.time));
return d.format("lll");
}
}
} catch (error) {}
let results = props.c2pa?.manifest_info.validation_results?.activeManifest;
if (results) {
valid.value = results.failure.length == 0 && results.success.length > 0;
}
} catch (error) {}
},
{ immediate: true }
);
const dateCreatedDisplay = computed(() => {
if (dateCreated.value) {
return dateCreated.value.format("lll");
}
return undefined;
});
const creator = computed(() => {
try {
const manifests = Object.values(props.c2pa?.manifest_info.manifests ?? {});
for (const manifest of manifests) {
for (const assertion of manifest.assertions) {
if (assertion.label === "c2pa.actions") {
const actions = (assertion.data as C2PAActionsAssertion)?.actions ?? [];
const a = actions.find((a) => a.action === "c2pa.created");
if (a) {
return a.softwareAgent;
}
}
return false;
}
}
} catch (error) {}
return undefined;
const showAgeWarning = computed(() => {
if (dateCreated.value) {
return dayjs().diff(dateCreated.value, "month") >= 3;
}
return false;
});
</script>