catalog: get date from XMP as fallback from Exif

This commit is contained in:
Thibault Deckers 2020-11-30 15:09:32 +09:00
parent 93af6b0d1b
commit 8c5a600151
5 changed files with 40 additions and 8 deletions

View file

@ -44,6 +44,7 @@ import deckers.thibault.aves.metadata.MetadataExtractorHelper.getSafeInt
import deckers.thibault.aves.metadata.MetadataExtractorHelper.getSafeRational
import deckers.thibault.aves.metadata.MetadataExtractorHelper.getSafeString
import deckers.thibault.aves.metadata.XMP
import deckers.thibault.aves.metadata.XMP.getSafeDateMillis
import deckers.thibault.aves.metadata.XMP.getSafeLocalizedText
import deckers.thibault.aves.utils.BitmapUtils
import deckers.thibault.aves.utils.BitmapUtils.getBytes
@ -205,6 +206,13 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
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
private fun getCatalogMetadataByMetadataExtractor(uri: Uri, mimeType: String, path: String?): Map<String, Any> {
val metadataMap = HashMap<String, Any>()
@ -270,9 +278,12 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
val values = (1 until count + 1).map { xmpMeta.getArrayItem(XMP.DC_SCHEMA_NS, XMP.SUBJECT_PROP_NAME, it).value }
metadataMap[KEY_XMP_SUBJECTS] = values.joinToString(separator = ";")
}
xmpMeta.getSafeLocalizedText(XMP.TITLE_PROP_NAME) { metadataMap[KEY_XMP_TITLE_DESCRIPTION] = it }
xmpMeta.getSafeLocalizedText(XMP.DC_SCHEMA_NS, XMP.TITLE_PROP_NAME) { metadataMap[KEY_XMP_TITLE_DESCRIPTION] = it }
if (!metadataMap.containsKey(KEY_XMP_TITLE_DESCRIPTION)) {
xmpMeta.getSafeLocalizedText(XMP.DESCRIPTION_PROP_NAME) { metadataMap[KEY_XMP_TITLE_DESCRIPTION] = it }
xmpMeta.getSafeLocalizedText(XMP.DC_SCHEMA_NS, XMP.DESCRIPTION_PROP_NAME) { metadataMap[KEY_XMP_TITLE_DESCRIPTION] = it }
}
if (!metadataMap.containsKey(KEY_DATE_MILLIS)) {
xmpMeta.getSafeDateMillis(XMP.XMP_SCHEMA_NS, XMP.CREATE_DATE_PROP_NAME) { metadataMap[KEY_DATE_MILLIS] = it }
}
} catch (e: XMPException) {
Log.w(LOG_TAG, "failed to read XMP directory for uri=$uri", e)

View file

@ -41,6 +41,8 @@ object Metadata {
}
}
// not sure which standards are used for the different video formats,
// but looks like some form of ISO 8601 `basic format`:
// yyyyMMddTHHmmss(.sss)?(Z|+/-hhmm)?
fun parseVideoMetadataDate(metadataDate: String?): Long {
var dateString = metadataDate ?: return 0

View file

@ -4,6 +4,7 @@ import android.util.Log
import com.adobe.internal.xmp.XMPException
import com.adobe.internal.xmp.XMPMeta
import deckers.thibault.aves.utils.LogUtils
import java.util.*
object XMP {
private val LOG_TAG = LogUtils.createTag(XMP::class.java)
@ -14,20 +15,38 @@ object XMP {
const val SUBJECT_PROP_NAME = "dc:subject"
const val TITLE_PROP_NAME = "dc:title"
const val DESCRIPTION_PROP_NAME = "dc:description"
const val CREATE_DATE_PROP_NAME = "xmp:CreateDate"
const val THUMBNAIL_PROP_NAME = "xmp:Thumbnails"
const val THUMBNAIL_IMAGE_PROP_NAME = "xmpGImg:image"
private const val GENERIC_LANG = ""
private const val SPECIFIC_LANG = "en-US"
fun XMPMeta.getSafeLocalizedText(propName: String, save: (value: String) -> Unit) {
fun XMPMeta.getSafeLocalizedText(schema: String, propName: String, save: (value: String) -> Unit) {
try {
if (this.doesPropertyExist(DC_SCHEMA_NS, propName)) {
val item = this.getLocalizedText(DC_SCHEMA_NS, propName, GENERIC_LANG, SPECIFIC_LANG)
if (this.doesPropertyExist(schema, propName)) {
val item = this.getLocalizedText(schema, propName, GENERIC_LANG, SPECIFIC_LANG)
// double check retrieved items as the property sometimes is reported to exist but it is actually null
if (item != null) save(item.value)
}
} catch (e: XMPException) {
Log.w(LOG_TAG, "failed to get text for XMP propName=$propName", e)
Log.w(LOG_TAG, "failed to get text for XMP schema=$schema, propName=$propName", e)
}
}
fun XMPMeta.getSafeDateMillis(schema: String, propName: String, save: (value: Long) -> Unit) {
try {
if (this.doesPropertyExist(schema, propName)) {
val item = this.getPropertyDate(schema, propName)
// double check retrieved items as the property sometimes is reported to exist but it is actually null
if (item != null) {
// strip time zone from XMP dates so that we show date/times as local ones
// this aligns with Exif date/times, which are specified without time zones
item.timeZone = TimeZone.getDefault()
save(item.calendar.timeInMillis)
}
}
} catch (e: XMPException) {
Log.w(LOG_TAG, "failed to get text for XMP schema=$schema, propName=$propName", e)
}
}
}

View file

@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.4.10'
ext.kotlin_version = '1.4.20'
repositories {
google()
jcenter()

View file

@ -299,7 +299,7 @@ class ImageEntry {
bool get isLocated => _addressDetails != null;
LatLng get latLng => isCatalogued ? LatLng(_catalogMetadata.latitude, _catalogMetadata.longitude) : null;
LatLng get latLng => hasGps ? LatLng(_catalogMetadata.latitude, _catalogMetadata.longitude) : null;
String get geoUri {
if (!hasGps) return null;