@@ -165,10 +165,9 @@ def get_meta_iptc(file_stream: BinaryIO):
165165 except KeyError :
166166 continue
167167 if isinstance (value , list ):
168- value = [decode (v ) for v in value ]
168+ metadata [ tag ] = [decode (v ) for v in value ]
169169 elif isinstance (value , bytes ):
170- value = decode (value )
171- metadata [tag ] = value
170+ metadata [tag ] = decode (value )
172171 return metadata
173172
174173
@@ -177,8 +176,18 @@ def read_metadata(input: bytes) -> MediaMetadata:
177176
178177 @param file_stream: stream
179178 """
180- with pyexiv2 .ImageData (input ) as img :
181- xmp = img .read_xmp ()
179+ try :
180+ with pyexiv2 .ImageData (input ) as img :
181+ xmp = img .read_xmp ()
182+ except (RuntimeError , ValueError ) as e :
183+ logger .warning (f"Failed to read image metadata with pyexiv2: { e } , trying exiftool fallback" , exc_info = True )
184+ # Try to use exiftool as fallback when pyexiv2 fails
185+ # Import here to avoid loading exiftool dependencies unless needed (lazy loading)
186+ from superdesk .media .video import read_metadata as read_metadata_video
187+
188+ # Map video module metadata to image module field names
189+ return _map_video_to_image (read_metadata_video (input ))
190+
182191 return {
183192 "Description" : get_xmp_lang_string (xmp .get ("Xmp.dc.description" )),
184193 "DescriptionWriter" : xmp .get ("Xmp.photoshop.CaptionWriter" , "" ),
@@ -197,6 +206,57 @@ def read_metadata(input: bytes) -> MediaMetadata:
197206 }
198207
199208
209+ # Field mappings between image module and video module field names
210+ # Keys are video module names, values are image module names
211+ _FIELD_MAPPING : Dict [str , str ] = {
212+ "CaptionWriter" : "DescriptionWriter" ,
213+ "TransmissionReference" : "JobId" ,
214+ "AuthorsPosition" : "CreatorsJobtitle" ,
215+ "Rights" : "CopyrightNotice" ,
216+ "State" : "ProvinceState" ,
217+ "Credit" : "CreditLine" ,
218+ }
219+
220+
221+ def _map_video_to_image (metadata : MediaMetadata ) -> MediaMetadata :
222+ """Map video module metadata field names to image module field names.
223+
224+ Converts video module field names to image module field names for consistency.
225+
226+ @param metadata: Metadata dict from exiftool fallback
227+ @return: Metadata with image module field names
228+ """
229+ result : MediaMetadata = {}
230+
231+ for key , value in metadata .items ():
232+ # Use mapped name if available, otherwise keep original name
233+ mapped_key = _FIELD_MAPPING .get (key , key )
234+ result [mapped_key ] = value # type: ignore[literal-required]
235+
236+ return result
237+
238+
239+ def _map_image_to_video (metadata : MediaMetadata ) -> MediaMetadata :
240+ """Map image module metadata field names to video module field names.
241+
242+ Converts image module field names back to video module field names for
243+ compatibility with the video module's exiftool handler.
244+
245+ @param metadata: Metadata dict with image module field names
246+ @return: Metadata with video module field names
247+ """
248+ # Create reverse mapping: image module field names to video module field names
249+ reverse_mapping = {v : k for k , v in _FIELD_MAPPING .items ()}
250+
251+ result : MediaMetadata = {}
252+ for key , value in metadata .items ():
253+ # Use reverse mapped name if available, otherwise keep original name
254+ mapped_key = reverse_mapping .get (key , key )
255+ result [mapped_key ] = value # type: ignore[literal-required]
256+
257+ return result
258+
259+
200260def get_xmp_lang_string (value , lang = "x-default" ):
201261 lang_key = 'lang="{}"' .format (lang )
202262 if value and isinstance (value , dict ) and value .get (lang_key ):
@@ -234,7 +294,17 @@ def write_metadata(input: bytes, metadata: MediaMetadata) -> bytes:
234294 xmp = {k : v for k , v in xmp .items () if v }
235295 iptc = convert_xmp_to_iptc (xmp )
236296
237- with pyexiv2 .ImageData (input ) as img :
238- img .modify_xmp (xmp )
239- img .modify_iptc (iptc )
240- return img .get_bytes ()
297+ try :
298+ with pyexiv2 .ImageData (input ) as img :
299+ img .modify_xmp (xmp )
300+ img .modify_iptc (iptc )
301+ return img .get_bytes ()
302+ except (RuntimeError , ValueError ) as e :
303+ logger .warning (f"Failed to write image metadata with pyexiv2: { e } , trying exiftool fallback" , exc_info = True )
304+ # Try to use exiftool as fallback when pyexiv2 fails
305+ # Import here to avoid loading exiftool dependencies unless needed (lazy loading)
306+ from superdesk .media .video import write_metadata as write_metadata_video
307+
308+ # Map image module metadata to video module field names
309+ mapped_metadata = _map_image_to_video (metadata )
310+ return write_metadata_video (input , mapped_metadata )
0 commit comments