c2pa-python 0.29.0__tar.gz → 0.30.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {c2pa_python-0.29.0/src/c2pa_python.egg-info → c2pa_python-0.30.0}/PKG-INFO +1 -1
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/pyproject.toml +1 -1
- {c2pa_python-0.29.0 → c2pa_python-0.30.0/src/c2pa_python.egg-info}/PKG-INFO +1 -1
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/tests/test_unit_tests.py +480 -1
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/LICENSE-APACHE +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/LICENSE-MIT +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/MANIFEST.in +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/README.md +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/requirements.txt +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/scripts/download_artifacts.py +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/setup.cfg +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/setup.py +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/src/c2pa/__init__.py +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/src/c2pa/build.py +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/src/c2pa/c2pa.py +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/src/c2pa/lib.py +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/src/c2pa_python.egg-info/SOURCES.txt +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/src/c2pa_python.egg-info/dependency_links.txt +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/src/c2pa_python.egg-info/entry_points.txt +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/src/c2pa_python.egg-info/requires.txt +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/src/c2pa_python.egg-info/top_level.txt +0 -0
- {c2pa_python-0.29.0 → c2pa_python-0.30.0}/tests/test_unit_tests_threaded.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c2pa-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.0
|
|
4
4
|
Summary: Python bindings for the C2PA Content Authenticity Initiative (CAI) library
|
|
5
5
|
Author-email: Gavin Peacock <gvnpeacock@adobe.com>, Tania Mathern <mathern@adobe.com>
|
|
6
6
|
Maintainer-email: Gavin Peacock <gpeacock@adobe.com>
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "c2pa-python"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.30.0"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
description = "Python bindings for the C2PA Content Authenticity Initiative (CAI) library"
|
|
10
10
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c2pa-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.0
|
|
4
4
|
Summary: Python bindings for the C2PA Content Authenticity Initiative (CAI) library
|
|
5
5
|
Author-email: Gavin Peacock <gvnpeacock@adobe.com>, Tania Mathern <mathern@adobe.com>
|
|
6
6
|
Maintainer-email: Gavin Peacock <gpeacock@adobe.com>
|
|
@@ -70,7 +70,7 @@ def load_test_settings_json():
|
|
|
70
70
|
class TestC2paSdk(unittest.TestCase):
|
|
71
71
|
def test_sdk_version(self):
|
|
72
72
|
# This test verifies the native libraries used match the expected version.
|
|
73
|
-
self.assertIn("0.78.
|
|
73
|
+
self.assertIn("0.78.6", sdk_version())
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
class TestReader(unittest.TestCase):
|
|
@@ -1021,6 +1021,35 @@ class TestBuilderWithSigner(unittest.TestCase):
|
|
|
1021
1021
|
return signature
|
|
1022
1022
|
self.callback_signer_es256 = callback_signer_es256
|
|
1023
1023
|
|
|
1024
|
+
def _create_ingredient_archive(self, ingredient_json=None):
|
|
1025
|
+
"""Helper: create an ingredient archive from a single ingredient."""
|
|
1026
|
+
if ingredient_json is None:
|
|
1027
|
+
ingredient_json = {"title": "photo.jpg", "relationship": "componentOf"}
|
|
1028
|
+
manifest = {
|
|
1029
|
+
"claim_generator_info": [{"name": "c2pa-test", "version": "1.0"}],
|
|
1030
|
+
"assertions": [
|
|
1031
|
+
{
|
|
1032
|
+
"label": "c2pa.actions",
|
|
1033
|
+
"data": {
|
|
1034
|
+
"actions": [
|
|
1035
|
+
{
|
|
1036
|
+
"action": "c2pa.created",
|
|
1037
|
+
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCreation",
|
|
1038
|
+
}
|
|
1039
|
+
]
|
|
1040
|
+
},
|
|
1041
|
+
}
|
|
1042
|
+
],
|
|
1043
|
+
}
|
|
1044
|
+
builder = Builder.from_json(manifest)
|
|
1045
|
+
with open(self.testPath, "rb") as f:
|
|
1046
|
+
builder.add_ingredient(ingredient_json, "image/jpeg", f)
|
|
1047
|
+
archive = io.BytesIO()
|
|
1048
|
+
builder.to_archive(archive)
|
|
1049
|
+
builder.close()
|
|
1050
|
+
archive.seek(0)
|
|
1051
|
+
return archive
|
|
1052
|
+
|
|
1024
1053
|
def test_can_retrieve_builder_supported_mimetypes(self):
|
|
1025
1054
|
result1 = Builder.get_supported_mime_types()
|
|
1026
1055
|
self.assertTrue(len(result1) > 0)
|
|
@@ -4151,6 +4180,456 @@ class TestBuilderWithSigner(unittest.TestCase):
|
|
|
4151
4180
|
# Make sure settings are put back to the common test defaults
|
|
4152
4181
|
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled":false},"auto_opened_action":{"enabled":false},"auto_created_action":{"enabled":false}}}}')
|
|
4153
4182
|
|
|
4183
|
+
def test_link_archive_label_on_signing_builder_placed(self):
|
|
4184
|
+
"""Label set on the signing builder's add_ingredient links an
|
|
4185
|
+
ingredient archive to a c2pa.placed action."""
|
|
4186
|
+
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled":false},"auto_opened_action":{"enabled":false},"auto_created_action":{"enabled":false}}}}')
|
|
4187
|
+
|
|
4188
|
+
archive = self._create_ingredient_archive()
|
|
4189
|
+
|
|
4190
|
+
manifest = {
|
|
4191
|
+
"claim_generator_info": [{"name": "c2pa-test", "version": "1.0"}],
|
|
4192
|
+
"assertions": [
|
|
4193
|
+
{
|
|
4194
|
+
"label": "c2pa.actions.v2",
|
|
4195
|
+
"data": {
|
|
4196
|
+
"actions": [
|
|
4197
|
+
{
|
|
4198
|
+
"action": "c2pa.placed",
|
|
4199
|
+
"parameters": {
|
|
4200
|
+
"ingredientIds": ["my-ingredient"]
|
|
4201
|
+
},
|
|
4202
|
+
}
|
|
4203
|
+
]
|
|
4204
|
+
},
|
|
4205
|
+
}
|
|
4206
|
+
],
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
builder = Builder.from_json(manifest)
|
|
4210
|
+
builder.add_ingredient(
|
|
4211
|
+
{"title": "photo.jpg", "relationship": "componentOf", "label": "my-ingredient"},
|
|
4212
|
+
"application/c2pa",
|
|
4213
|
+
archive,
|
|
4214
|
+
)
|
|
4215
|
+
|
|
4216
|
+
with open(self.testPath, "rb") as src:
|
|
4217
|
+
output = io.BytesIO()
|
|
4218
|
+
builder.sign(self.signer, "image/jpeg", src, output)
|
|
4219
|
+
output.seek(0)
|
|
4220
|
+
|
|
4221
|
+
reader = Reader("image/jpeg", output)
|
|
4222
|
+
manifest_data = json.loads(reader.json())
|
|
4223
|
+
active = manifest_data["active_manifest"]
|
|
4224
|
+
assertions = manifest_data["manifests"][active]["assertions"]
|
|
4225
|
+
|
|
4226
|
+
placed_action = None
|
|
4227
|
+
for assertion in assertions:
|
|
4228
|
+
if assertion.get("label") == "c2pa.actions.v2":
|
|
4229
|
+
for action in assertion["data"]["actions"]:
|
|
4230
|
+
if action["action"] == "c2pa.placed":
|
|
4231
|
+
placed_action = action
|
|
4232
|
+
break
|
|
4233
|
+
|
|
4234
|
+
self.assertIsNotNone(placed_action, "c2pa.placed action not found")
|
|
4235
|
+
self.assertIn("parameters", placed_action)
|
|
4236
|
+
self.assertIn("ingredients", placed_action["parameters"])
|
|
4237
|
+
self.assertEqual(len(placed_action["parameters"]["ingredients"]), 1)
|
|
4238
|
+
self.assertIn(
|
|
4239
|
+
"c2pa.ingredient.v3",
|
|
4240
|
+
placed_action["parameters"]["ingredients"][0]["url"],
|
|
4241
|
+
)
|
|
4242
|
+
|
|
4243
|
+
reader.close()
|
|
4244
|
+
output.close()
|
|
4245
|
+
archive.close()
|
|
4246
|
+
builder.close()
|
|
4247
|
+
|
|
4248
|
+
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled":false},"auto_opened_action":{"enabled":false},"auto_created_action":{"enabled":false}}}}')
|
|
4249
|
+
|
|
4250
|
+
def test_link_archive_label_on_signing_builder_opened(self):
|
|
4251
|
+
"""Label set on the signing builder's add_ingredient links an
|
|
4252
|
+
ingredient archive to a c2pa.opened action."""
|
|
4253
|
+
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled":false},"auto_opened_action":{"enabled":false},"auto_created_action":{"enabled":false}}}}')
|
|
4254
|
+
|
|
4255
|
+
archive = self._create_ingredient_archive(
|
|
4256
|
+
{"title": "photo.jpg", "relationship": "parentOf"}
|
|
4257
|
+
)
|
|
4258
|
+
|
|
4259
|
+
manifest = {
|
|
4260
|
+
"claim_generator_info": [{"name": "c2pa-test", "version": "1.0"}],
|
|
4261
|
+
"assertions": [
|
|
4262
|
+
{
|
|
4263
|
+
"label": "c2pa.actions.v2",
|
|
4264
|
+
"data": {
|
|
4265
|
+
"actions": [
|
|
4266
|
+
{
|
|
4267
|
+
"action": "c2pa.opened",
|
|
4268
|
+
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCreation",
|
|
4269
|
+
"parameters": {
|
|
4270
|
+
"ingredientIds": ["my-ingredient"]
|
|
4271
|
+
},
|
|
4272
|
+
}
|
|
4273
|
+
]
|
|
4274
|
+
},
|
|
4275
|
+
}
|
|
4276
|
+
],
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
builder = Builder.from_json(manifest)
|
|
4280
|
+
builder.add_ingredient(
|
|
4281
|
+
{"title": "photo.jpg", "relationship": "parentOf", "label": "my-ingredient"},
|
|
4282
|
+
"application/c2pa",
|
|
4283
|
+
archive,
|
|
4284
|
+
)
|
|
4285
|
+
|
|
4286
|
+
with open(self.testPath, "rb") as src:
|
|
4287
|
+
output = io.BytesIO()
|
|
4288
|
+
builder.sign(self.signer, "image/jpeg", src, output)
|
|
4289
|
+
output.seek(0)
|
|
4290
|
+
|
|
4291
|
+
reader = Reader("image/jpeg", output)
|
|
4292
|
+
manifest_data = json.loads(reader.json())
|
|
4293
|
+
active = manifest_data["active_manifest"]
|
|
4294
|
+
assertions = manifest_data["manifests"][active]["assertions"]
|
|
4295
|
+
|
|
4296
|
+
opened_action = None
|
|
4297
|
+
for assertion in assertions:
|
|
4298
|
+
if assertion.get("label") == "c2pa.actions.v2":
|
|
4299
|
+
for action in assertion["data"]["actions"]:
|
|
4300
|
+
if action["action"] == "c2pa.opened":
|
|
4301
|
+
opened_action = action
|
|
4302
|
+
break
|
|
4303
|
+
|
|
4304
|
+
self.assertIsNotNone(opened_action, "c2pa.opened action not found")
|
|
4305
|
+
self.assertIn("parameters", opened_action)
|
|
4306
|
+
self.assertIn("ingredients", opened_action["parameters"])
|
|
4307
|
+
self.assertEqual(len(opened_action["parameters"]["ingredients"]), 1)
|
|
4308
|
+
self.assertIn(
|
|
4309
|
+
"c2pa.ingredient.v3",
|
|
4310
|
+
opened_action["parameters"]["ingredients"][0]["url"],
|
|
4311
|
+
)
|
|
4312
|
+
|
|
4313
|
+
reader.close()
|
|
4314
|
+
output.close()
|
|
4315
|
+
archive.close()
|
|
4316
|
+
builder.close()
|
|
4317
|
+
|
|
4318
|
+
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled":false},"auto_opened_action":{"enabled":false},"auto_created_action":{"enabled":false}}}}')
|
|
4319
|
+
|
|
4320
|
+
def test_link_archive_two_ingredients_labels(self):
|
|
4321
|
+
"""Two ingredient archives linked to two different actions via
|
|
4322
|
+
distinct labels. Verifies no cross-linking."""
|
|
4323
|
+
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled":false},"auto_opened_action":{"enabled":false},"auto_created_action":{"enabled":false}}}}')
|
|
4324
|
+
|
|
4325
|
+
archive1 = self._create_ingredient_archive(
|
|
4326
|
+
{"title": "photo-placed.jpg", "relationship": "componentOf"}
|
|
4327
|
+
)
|
|
4328
|
+
archive2 = self._create_ingredient_archive(
|
|
4329
|
+
{"title": "photo-opened.jpg", "relationship": "parentOf"}
|
|
4330
|
+
)
|
|
4331
|
+
|
|
4332
|
+
manifest = {
|
|
4333
|
+
"claim_generator_info": [{"name": "c2pa-test", "version": "1.0"}],
|
|
4334
|
+
"assertions": [
|
|
4335
|
+
{
|
|
4336
|
+
"label": "c2pa.actions.v2",
|
|
4337
|
+
"data": {
|
|
4338
|
+
"actions": [
|
|
4339
|
+
{
|
|
4340
|
+
"action": "c2pa.placed",
|
|
4341
|
+
"parameters": {
|
|
4342
|
+
"ingredientIds": ["ingredient-for-placed"]
|
|
4343
|
+
},
|
|
4344
|
+
},
|
|
4345
|
+
{
|
|
4346
|
+
"action": "c2pa.opened",
|
|
4347
|
+
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCreation",
|
|
4348
|
+
"parameters": {
|
|
4349
|
+
"ingredientIds": ["ingredient-for-opened"]
|
|
4350
|
+
},
|
|
4351
|
+
},
|
|
4352
|
+
]
|
|
4353
|
+
},
|
|
4354
|
+
}
|
|
4355
|
+
],
|
|
4356
|
+
}
|
|
4357
|
+
|
|
4358
|
+
builder = Builder.from_json(manifest)
|
|
4359
|
+
builder.add_ingredient(
|
|
4360
|
+
{"title": "photo-placed.jpg", "relationship": "componentOf", "label": "ingredient-for-placed"},
|
|
4361
|
+
"application/c2pa",
|
|
4362
|
+
archive1,
|
|
4363
|
+
)
|
|
4364
|
+
builder.add_ingredient(
|
|
4365
|
+
{"title": "photo-opened.jpg", "relationship": "parentOf", "label": "ingredient-for-opened"},
|
|
4366
|
+
"application/c2pa",
|
|
4367
|
+
archive2,
|
|
4368
|
+
)
|
|
4369
|
+
|
|
4370
|
+
with open(self.testPath, "rb") as src:
|
|
4371
|
+
output = io.BytesIO()
|
|
4372
|
+
builder.sign(self.signer, "image/jpeg", src, output)
|
|
4373
|
+
output.seek(0)
|
|
4374
|
+
|
|
4375
|
+
reader = Reader("image/jpeg", output)
|
|
4376
|
+
manifest_data = json.loads(reader.json())
|
|
4377
|
+
active = manifest_data["active_manifest"]
|
|
4378
|
+
assertions = manifest_data["manifests"][active]["assertions"]
|
|
4379
|
+
|
|
4380
|
+
placed_action = None
|
|
4381
|
+
opened_action = None
|
|
4382
|
+
for assertion in assertions:
|
|
4383
|
+
if assertion.get("label") == "c2pa.actions.v2":
|
|
4384
|
+
for action in assertion["data"]["actions"]:
|
|
4385
|
+
if action["action"] == "c2pa.placed":
|
|
4386
|
+
placed_action = action
|
|
4387
|
+
if action["action"] == "c2pa.opened":
|
|
4388
|
+
opened_action = action
|
|
4389
|
+
|
|
4390
|
+
self.assertIsNotNone(placed_action, "c2pa.placed action not found")
|
|
4391
|
+
self.assertIsNotNone(opened_action, "c2pa.opened action not found")
|
|
4392
|
+
|
|
4393
|
+
self.assertIn("ingredients", placed_action["parameters"])
|
|
4394
|
+
self.assertEqual(len(placed_action["parameters"]["ingredients"]), 1)
|
|
4395
|
+
placed_url = placed_action["parameters"]["ingredients"][0]["url"]
|
|
4396
|
+
|
|
4397
|
+
self.assertIn("ingredients", opened_action["parameters"])
|
|
4398
|
+
self.assertEqual(len(opened_action["parameters"]["ingredients"]), 1)
|
|
4399
|
+
opened_url = opened_action["parameters"]["ingredients"][0]["url"]
|
|
4400
|
+
|
|
4401
|
+
# Each action should link to a different ingredient (no cross-linking)
|
|
4402
|
+
self.assertNotEqual(placed_url, opened_url,
|
|
4403
|
+
"Each action should link to a different ingredient")
|
|
4404
|
+
|
|
4405
|
+
reader.close()
|
|
4406
|
+
output.close()
|
|
4407
|
+
archive1.close()
|
|
4408
|
+
archive2.close()
|
|
4409
|
+
builder.close()
|
|
4410
|
+
|
|
4411
|
+
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled":false},"auto_opened_action":{"enabled":false},"auto_created_action":{"enabled":false}}}}')
|
|
4412
|
+
|
|
4413
|
+
def test_link_archive_multiple_ingredients_in_one_placed_action(self):
|
|
4414
|
+
"""A single c2pa.placed action references two componentOf ingredients
|
|
4415
|
+
via ingredientIds with two labels."""
|
|
4416
|
+
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled":false},"auto_opened_action":{"enabled":false},"auto_created_action":{"enabled":false}}}}')
|
|
4417
|
+
|
|
4418
|
+
archive1 = self._create_ingredient_archive(
|
|
4419
|
+
{"title": "base-layer.jpg", "relationship": "componentOf"}
|
|
4420
|
+
)
|
|
4421
|
+
archive2 = self._create_ingredient_archive(
|
|
4422
|
+
{"title": "overlay-layer.jpg", "relationship": "componentOf"}
|
|
4423
|
+
)
|
|
4424
|
+
|
|
4425
|
+
manifest = {
|
|
4426
|
+
"claim_generator_info": [{"name": "c2pa-test", "version": "1.0"}],
|
|
4427
|
+
"assertions": [
|
|
4428
|
+
{
|
|
4429
|
+
"label": "c2pa.actions.v2",
|
|
4430
|
+
"data": {
|
|
4431
|
+
"actions": [
|
|
4432
|
+
{
|
|
4433
|
+
"action": "c2pa.placed",
|
|
4434
|
+
"parameters": {
|
|
4435
|
+
"ingredientIds": ["base-layer", "overlay-layer"]
|
|
4436
|
+
},
|
|
4437
|
+
}
|
|
4438
|
+
]
|
|
4439
|
+
},
|
|
4440
|
+
}
|
|
4441
|
+
],
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4444
|
+
builder = Builder.from_json(manifest)
|
|
4445
|
+
builder.add_ingredient(
|
|
4446
|
+
{"title": "base-layer.jpg", "relationship": "componentOf", "label": "base-layer"},
|
|
4447
|
+
"application/c2pa",
|
|
4448
|
+
archive1,
|
|
4449
|
+
)
|
|
4450
|
+
builder.add_ingredient(
|
|
4451
|
+
{"title": "overlay-layer.jpg", "relationship": "componentOf", "label": "overlay-layer"},
|
|
4452
|
+
"application/c2pa",
|
|
4453
|
+
archive2,
|
|
4454
|
+
)
|
|
4455
|
+
|
|
4456
|
+
with open(self.testPath, "rb") as src:
|
|
4457
|
+
output = io.BytesIO()
|
|
4458
|
+
builder.sign(self.signer, "image/jpeg", src, output)
|
|
4459
|
+
output.seek(0)
|
|
4460
|
+
|
|
4461
|
+
reader = Reader("image/jpeg", output)
|
|
4462
|
+
manifest_data = json.loads(reader.json())
|
|
4463
|
+
active = manifest_data["active_manifest"]
|
|
4464
|
+
assertions = manifest_data["manifests"][active]["assertions"]
|
|
4465
|
+
|
|
4466
|
+
placed_action = None
|
|
4467
|
+
for assertion in assertions:
|
|
4468
|
+
if assertion.get("label") == "c2pa.actions.v2":
|
|
4469
|
+
for action in assertion["data"]["actions"]:
|
|
4470
|
+
if action["action"] == "c2pa.placed":
|
|
4471
|
+
placed_action = action
|
|
4472
|
+
break
|
|
4473
|
+
|
|
4474
|
+
self.assertIsNotNone(placed_action, "c2pa.placed action not found")
|
|
4475
|
+
self.assertIn("parameters", placed_action)
|
|
4476
|
+
self.assertIn("ingredients", placed_action["parameters"])
|
|
4477
|
+
ingredients = placed_action["parameters"]["ingredients"]
|
|
4478
|
+
self.assertEqual(len(ingredients), 2,
|
|
4479
|
+
"c2pa.placed should reference both ingredients")
|
|
4480
|
+
|
|
4481
|
+
url0 = ingredients[0]["url"]
|
|
4482
|
+
url1 = ingredients[1]["url"]
|
|
4483
|
+
self.assertNotEqual(url0, url1,
|
|
4484
|
+
"Each ingredient should have a distinct URL")
|
|
4485
|
+
|
|
4486
|
+
reader.close()
|
|
4487
|
+
output.close()
|
|
4488
|
+
archive1.close()
|
|
4489
|
+
archive2.close()
|
|
4490
|
+
builder.close()
|
|
4491
|
+
|
|
4492
|
+
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled":false},"auto_opened_action":{"enabled":false},"auto_created_action":{"enabled":false}}}}')
|
|
4493
|
+
|
|
4494
|
+
def test_ingredient_fields_survive_archive(self):
|
|
4495
|
+
archive = self._create_ingredient_archive({
|
|
4496
|
+
"title": "tracked-asset.jpg",
|
|
4497
|
+
"relationship": "componentOf",
|
|
4498
|
+
"instance_id": "tracking:project-7:asset-42",
|
|
4499
|
+
"description": "A tracked ingredient",
|
|
4500
|
+
"informational_URI": "https://example.com/assets/42",
|
|
4501
|
+
})
|
|
4502
|
+
|
|
4503
|
+
reader = Reader("application/c2pa", archive)
|
|
4504
|
+
manifest_data = json.loads(reader.json())
|
|
4505
|
+
active = manifest_data["active_manifest"]
|
|
4506
|
+
ingredients = manifest_data["manifests"][active]["ingredients"]
|
|
4507
|
+
|
|
4508
|
+
self.assertGreaterEqual(len(ingredients), 1)
|
|
4509
|
+
ing = ingredients[0]
|
|
4510
|
+
|
|
4511
|
+
self.assertEqual(ing["title"], "tracked-asset.jpg")
|
|
4512
|
+
self.assertIn("instance_id", ing)
|
|
4513
|
+
self.assertEqual(ing["instance_id"], "tracking:project-7:asset-42")
|
|
4514
|
+
|
|
4515
|
+
reader.close()
|
|
4516
|
+
archive.close()
|
|
4517
|
+
|
|
4518
|
+
def test_ingredient_fields_survive_archive_then_sign(self):
|
|
4519
|
+
"""instance_id set on the archive ingredient persists through
|
|
4520
|
+
archive then sign."""
|
|
4521
|
+
archive = self._create_ingredient_archive({
|
|
4522
|
+
"title": "tracked-asset.jpg",
|
|
4523
|
+
"relationship": "componentOf",
|
|
4524
|
+
"instance_id": "tracking:project-7:asset-42",
|
|
4525
|
+
"description": "A tracked ingredient",
|
|
4526
|
+
"informational_URI": "https://example.com/assets/42",
|
|
4527
|
+
})
|
|
4528
|
+
|
|
4529
|
+
manifest = {
|
|
4530
|
+
"claim_generator_info": [{"name": "c2pa-test", "version": "1.0"}],
|
|
4531
|
+
"assertions": [
|
|
4532
|
+
{
|
|
4533
|
+
"label": "c2pa.actions",
|
|
4534
|
+
"data": {
|
|
4535
|
+
"actions": [
|
|
4536
|
+
{
|
|
4537
|
+
"action": "c2pa.created",
|
|
4538
|
+
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCreation",
|
|
4539
|
+
}
|
|
4540
|
+
]
|
|
4541
|
+
},
|
|
4542
|
+
}
|
|
4543
|
+
],
|
|
4544
|
+
}
|
|
4545
|
+
|
|
4546
|
+
builder = Builder.from_json(manifest)
|
|
4547
|
+
builder.add_ingredient(
|
|
4548
|
+
{"title": "tracked-asset.jpg", "relationship": "componentOf"},
|
|
4549
|
+
"application/c2pa",
|
|
4550
|
+
archive,
|
|
4551
|
+
)
|
|
4552
|
+
|
|
4553
|
+
with open(self.testPath, "rb") as src:
|
|
4554
|
+
output = io.BytesIO()
|
|
4555
|
+
builder.sign(self.signer, "image/jpeg", src, output)
|
|
4556
|
+
output.seek(0)
|
|
4557
|
+
|
|
4558
|
+
reader = Reader("image/jpeg", output)
|
|
4559
|
+
manifest_data = json.loads(reader.json())
|
|
4560
|
+
active = manifest_data["active_manifest"]
|
|
4561
|
+
ingredients = manifest_data["manifests"][active]["ingredients"]
|
|
4562
|
+
|
|
4563
|
+
self.assertGreaterEqual(len(ingredients), 1)
|
|
4564
|
+
ing = ingredients[0]
|
|
4565
|
+
|
|
4566
|
+
self.assertIn("instance_id", ing)
|
|
4567
|
+
self.assertEqual(ing["instance_id"], "tracking:project-7:asset-42")
|
|
4568
|
+
|
|
4569
|
+
reader.close()
|
|
4570
|
+
output.close()
|
|
4571
|
+
archive.close()
|
|
4572
|
+
builder.close()
|
|
4573
|
+
|
|
4574
|
+
def test_instance_id_as_ingredient_identifier_in_catalog(self):
|
|
4575
|
+
"""Two ingredients with different instance_id values in one archive.
|
|
4576
|
+
Read the archive back and select an ingredient by instance_id."""
|
|
4577
|
+
manifest = {
|
|
4578
|
+
"claim_generator_info": [{"name": "c2pa-test", "version": "1.0"}],
|
|
4579
|
+
"assertions": [
|
|
4580
|
+
{
|
|
4581
|
+
"label": "c2pa.actions",
|
|
4582
|
+
"data": {
|
|
4583
|
+
"actions": [
|
|
4584
|
+
{
|
|
4585
|
+
"action": "c2pa.created",
|
|
4586
|
+
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCreation",
|
|
4587
|
+
}
|
|
4588
|
+
]
|
|
4589
|
+
},
|
|
4590
|
+
}
|
|
4591
|
+
],
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4594
|
+
builder = Builder.from_json(manifest)
|
|
4595
|
+
with open(self.testPath, "rb") as f:
|
|
4596
|
+
builder.add_ingredient(
|
|
4597
|
+
{"title": "photo-A.jpg", "relationship": "componentOf",
|
|
4598
|
+
"instance_id": "catalog:photo-A"},
|
|
4599
|
+
"image/jpeg", f,
|
|
4600
|
+
)
|
|
4601
|
+
with open(self.testPath, "rb") as f:
|
|
4602
|
+
builder.add_ingredient(
|
|
4603
|
+
{"title": "photo-B.jpg", "relationship": "componentOf",
|
|
4604
|
+
"instance_id": "catalog:photo-B"},
|
|
4605
|
+
"image/jpeg", f,
|
|
4606
|
+
)
|
|
4607
|
+
|
|
4608
|
+
archive = io.BytesIO()
|
|
4609
|
+
builder.to_archive(archive)
|
|
4610
|
+
archive.seek(0)
|
|
4611
|
+
builder.close()
|
|
4612
|
+
|
|
4613
|
+
reader = Reader("application/c2pa", archive)
|
|
4614
|
+
manifest_data = json.loads(reader.json())
|
|
4615
|
+
active = manifest_data["active_manifest"]
|
|
4616
|
+
ingredients = manifest_data["manifests"][active]["ingredients"]
|
|
4617
|
+
|
|
4618
|
+
self.assertEqual(len(ingredients), 2)
|
|
4619
|
+
|
|
4620
|
+
found = None
|
|
4621
|
+
for ing in ingredients:
|
|
4622
|
+
if ing.get("instance_id") == "catalog:photo-B":
|
|
4623
|
+
found = ing
|
|
4624
|
+
break
|
|
4625
|
+
|
|
4626
|
+
self.assertIsNotNone(found,
|
|
4627
|
+
"Should find ingredient by instance_id 'catalog:photo-B' in archive")
|
|
4628
|
+
self.assertEqual(found["title"], "photo-B.jpg")
|
|
4629
|
+
|
|
4630
|
+
reader.close()
|
|
4631
|
+
archive.close()
|
|
4632
|
+
|
|
4154
4633
|
|
|
4155
4634
|
class TestStream(unittest.TestCase):
|
|
4156
4635
|
def setUp(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|