@@ -45,6 +45,11 @@ def format_geolocation(value: Optional[str], is_latitude: bool) -> Optional[str]
4545 if not value :
4646 return None
4747 d = Decimal (value )
48+ # Validate coordinate ranges - return None for invalid values to skip EXIF field
49+ if is_latitude and not (- 90 <= d <= 90 ):
50+ return None
51+ if not is_latitude and not (- 180 <= d <= 180 ):
52+ return None
4853 degrees = int (abs (d ))
4954 minutes = (abs (d ) - degrees ) * 60
5055 if is_latitude :
@@ -128,11 +133,13 @@ def create_assertion_metadata(
128133 }
129134 if isinstance (date_created , datetime ):
130135 metadata ['dc:date' ] = date_created .strftime ('%Y-%m-%dT%H:%M:%SZ' )
131- if latitude :
132- metadata ['exif:GPSLatitude' ] = format_geolocation (latitude , True )
133- if longitude :
134- metadata ['exif:GPSLongitude' ] = format_geolocation (longitude , False )
135- if date_captured :
136+ if latitude and longitude :
137+ formatted_lat = format_geolocation (latitude , True )
138+ formatted_lon = format_geolocation (longitude , False )
139+ if formatted_lat and formatted_lon :
140+ metadata ['exif:GPSLatitude' ] = formatted_lat
141+ metadata ['exif:GPSLongitude' ] = formatted_lon
142+ if isinstance (date_captured , datetime ):
136143 metadata ['exif:GPSTimeStamp' ] = date_captured .strftime ('%H:%M:%S' )
137144 metadata ['exif:DateTimeOriginal' ] = date_captured .strftime ('%Y:%m:%d %H:%M:%S' )
138145
@@ -218,8 +225,14 @@ def create_action_c2pa_opened(
218225 Args:
219226 asset_hex_hash: Hex-encoded SHA256 hash of the asset
220227 digital_source_type: Digital source type. Can be either:
221- - Short form: 'trainedAlgorithmicMedia' (IPTC namespace auto-prepended)
222- - Full URI: 'http://c2pa.org/digitalsourcetype/empty' (used as-is)
228+ - Short form (e.g., 'trainedAlgorithmicMedia', 'digitalCapture', 'negativeFilm'):
229+ The IPTC namespace 'http://cv.iptc.org/newscodes/digitalsourcetype/' will be prepended.
230+ Example: 'trainedAlgorithmicMedia' → 'http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia'
231+ - Full URI (starting with 'http://' or 'https://'):
232+ Used as-is. Can be from any namespace (IPTC or C2PA).
233+ Examples: 'http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture'
234+ 'http://c2pa.org/digitalsourcetype/empty'
235+ See IPTC NewsCodes: https://cv.iptc.org/newscodes/digitalsourcetype/
223236 software_agent: Name of the software that created/modified the asset
224237
225238 Returns:
@@ -243,7 +256,7 @@ def create_action_c2pa_opened(
243256 }
244257 if digital_source_type :
245258 # If full URI provided, use as-is; otherwise prepend IPTC namespace
246- if digital_source_type .startswith ('http://' ):
259+ if digital_source_type .startswith (( 'http://' , 'https://' ) ):
247260 action ['digitalSourceType' ] = digital_source_type
248261 else :
249262 action ['digitalSourceType' ] = (
0 commit comments