catalog: fallback date from XMP photoshop:DateCreated, fallback HEIF date from MMR

This commit is contained in:
Thibault Deckers 2020-12-08 11:21:56 +09:00
parent ca670e4ee9
commit b297fd5fe0
2 changed files with 36 additions and 23 deletions

View file

@ -200,6 +200,20 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
return dirMap return dirMap
} }
// set `KEY_DATE_MILLIS` from these fields (by precedence):
// - ME / Exif / DATETIME_ORIGINAL
// - ME / Exif / DATETIME
// - EI / Exif / DATETIME_ORIGINAL
// - EI / Exif / DATETIME
// - ME / XMP / xmp:CreateDate
// - ME / XMP / photoshop:DateCreated
// - MMR / METADATA_KEY_DATE
// set `KEY_XMP_TITLE_DESCRIPTION` from these fields (by precedence):
// - ME / XMP / dc:title
// - ME / XMP / dc:description
// set `KEY_XMP_SUBJECTS` from these fields (by precedence):
// - ME / XMP / dc:subject
// - ME / IPTC / keywords
private fun getCatalogMetadata(call: MethodCall, result: MethodChannel.Result) { private fun getCatalogMetadata(call: MethodCall, result: MethodChannel.Result) {
val mimeType = call.argument<String>("mimeType") val mimeType = call.argument<String>("mimeType")
val uri = call.argument<String>("uri")?.let { Uri.parse(it) } val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
@ -211,24 +225,14 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
} }
val metadataMap = HashMap(getCatalogMetadataByMetadataExtractor(uri, mimeType, path, sizeBytes)) val metadataMap = HashMap(getCatalogMetadataByMetadataExtractor(uri, mimeType, path, sizeBytes))
if (isVideo(mimeType)) { if (isMultimedia(mimeType)) {
metadataMap.putAll(getVideoCatalogMetadataByMediaMetadataRetriever(uri)) metadataMap.putAll(getMultimediaCatalogMetadataByMediaMetadataRetriever(uri))
} }
// report success even when empty // report success even when empty
result.success(metadataMap) result.success(metadataMap)
} }
// set `KEY_DATE_MILLIS` from these fields (by precedence):
// - Exif / DATETIME_ORIGINAL
// - Exif / DATETIME
// - XMP / xmp:CreateDate
// set `KEY_XMP_TITLE_DESCRIPTION` from these fields (by precedence):
// - XMP / dc:title
// - XMP / dc:description
// set `KEY_XMP_SUBJECTS` from these fields (by precedence):
// - XMP / dc:subject
// - IPTC / keywords
private fun getCatalogMetadataByMetadataExtractor(uri: Uri, mimeType: String, path: String?, sizeBytes: Long?): Map<String, Any> { private fun getCatalogMetadataByMetadataExtractor(uri: Uri, mimeType: String, path: String?, sizeBytes: Long?): Map<String, Any> {
val metadataMap = HashMap<String, Any>() val metadataMap = HashMap<String, Any>()
@ -301,6 +305,9 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
} }
if (!metadataMap.containsKey(KEY_DATE_MILLIS)) { if (!metadataMap.containsKey(KEY_DATE_MILLIS)) {
xmpMeta.getSafeDateMillis(XMP.XMP_SCHEMA_NS, XMP.CREATE_DATE_PROP_NAME) { metadataMap[KEY_DATE_MILLIS] = it } xmpMeta.getSafeDateMillis(XMP.XMP_SCHEMA_NS, XMP.CREATE_DATE_PROP_NAME) { metadataMap[KEY_DATE_MILLIS] = it }
if (!metadataMap.containsKey(KEY_DATE_MILLIS)) {
xmpMeta.getSafeDateMillis(XMP.PHOTOSHOP_SCHEMA_NS, XMP.PS_DATE_CREATED_PROP_NAME) { metadataMap[KEY_DATE_MILLIS] = it }
}
} }
// identification of panorama (aka photo sphere) // identification of panorama (aka photo sphere)
@ -381,22 +388,26 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
return metadataMap return metadataMap
} }
private fun getVideoCatalogMetadataByMediaMetadataRetriever(uri: Uri): Map<String, Any> { private fun getMultimediaCatalogMetadataByMediaMetadataRetriever(uri: Uri): Map<String, Any> {
val metadataMap = HashMap<String, Any>() val metadataMap = HashMap<String, Any>()
val retriever = StorageUtils.openMetadataRetriever(context, uri) ?: return metadataMap val retriever = StorageUtils.openMetadataRetriever(context, uri) ?: return metadataMap
try { try {
retriever.getSafeInt(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION) { metadataMap[KEY_ROTATION_DEGREES] = it } retriever.getSafeInt(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION) { metadataMap[KEY_ROTATION_DEGREES] = it }
retriever.getSafeDateMillis(MediaMetadataRetriever.METADATA_KEY_DATE) { metadataMap[KEY_DATE_MILLIS] = it } if (!metadataMap.containsKey(KEY_DATE_MILLIS)) {
retriever.getSafeDateMillis(MediaMetadataRetriever.METADATA_KEY_DATE) { metadataMap[KEY_DATE_MILLIS] = it }
}
val locationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION) if (!metadataMap.containsKey(KEY_LATITUDE)) {
if (locationString != null) { val locationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION)
val matcher = Metadata.VIDEO_LOCATION_PATTERN.matcher(locationString) if (locationString != null) {
if (matcher.find() && matcher.groupCount() >= 2) { val matcher = Metadata.VIDEO_LOCATION_PATTERN.matcher(locationString)
val latitude = matcher.group(1)?.toDoubleOrNull() if (matcher.find() && matcher.groupCount() >= 2) {
val longitude = matcher.group(2)?.toDoubleOrNull() val latitude = matcher.group(1)?.toDoubleOrNull()
if (latitude != null && longitude != null) { val longitude = matcher.group(2)?.toDoubleOrNull()
metadataMap[KEY_LATITUDE] = latitude if (latitude != null && longitude != null) {
metadataMap[KEY_LONGITUDE] = longitude metadataMap[KEY_LATITUDE] = latitude
metadataMap[KEY_LONGITUDE] = longitude
}
} }
} }
} }

View file

@ -10,12 +10,14 @@ object XMP {
private val LOG_TAG = LogUtils.createTag(XMP::class.java) private val LOG_TAG = LogUtils.createTag(XMP::class.java)
const val DC_SCHEMA_NS = "http://purl.org/dc/elements/1.1/" const val DC_SCHEMA_NS = "http://purl.org/dc/elements/1.1/"
const val PHOTOSHOP_SCHEMA_NS = "http://ns.adobe.com/photoshop/1.0/"
const val XMP_SCHEMA_NS = "http://ns.adobe.com/xap/1.0/" const val XMP_SCHEMA_NS = "http://ns.adobe.com/xap/1.0/"
const val IMG_SCHEMA_NS = "http://ns.adobe.com/xap/1.0/g/img/" const val IMG_SCHEMA_NS = "http://ns.adobe.com/xap/1.0/g/img/"
const val SUBJECT_PROP_NAME = "dc:subject" const val SUBJECT_PROP_NAME = "dc:subject"
const val TITLE_PROP_NAME = "dc:title" const val TITLE_PROP_NAME = "dc:title"
const val DESCRIPTION_PROP_NAME = "dc:description" const val DESCRIPTION_PROP_NAME = "dc:description"
const val PS_DATE_CREATED_PROP_NAME = "photoshop:DateCreated";
const val CREATE_DATE_PROP_NAME = "xmp:CreateDate" const val CREATE_DATE_PROP_NAME = "xmp:CreateDate"
const val THUMBNAIL_PROP_NAME = "xmp:Thumbnails" const val THUMBNAIL_PROP_NAME = "xmp:Thumbnails"
const val THUMBNAIL_IMAGE_PROP_NAME = "xmpGImg:image" const val THUMBNAIL_IMAGE_PROP_NAME = "xmpGImg:image"