diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt index 0d337c817..4fc0f41cc 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt @@ -22,6 +22,10 @@ import com.drew.metadata.exif.* import com.drew.metadata.file.FileTypeDirectory import com.drew.metadata.gif.GifAnimationDirectory import com.drew.metadata.iptc.IptcDirectory +import com.drew.metadata.mov.QuickTimeDirectory +import com.drew.metadata.mov.media.QuickTimeMediaDirectory +import com.drew.metadata.mp4.Mp4Directory +import com.drew.metadata.mp4.media.Mp4MediaDirectory import com.drew.metadata.mp4.media.Mp4UuidBoxDirectory import com.drew.metadata.png.PngDirectory import com.drew.metadata.webp.WebpDirectory @@ -76,6 +80,7 @@ import kotlinx.coroutines.launch import org.beyka.tiffbitmapfactory.TiffBitmapFactory import java.io.File import java.text.ParseException +import java.text.SimpleDateFormat import java.util.* import kotlin.math.roundToLong @@ -146,6 +151,33 @@ class MetadataHandler(private val context: Context) : MethodCallHandler { } else { dirMap.putAll(tags.map { tagMapper(it) }) } + } else if (dir is Mp4Directory || dir is QuickTimeDirectory) { + tags.map { tag -> + val tagName = tag.tagName + when (tag.tagType) { + Mp4Directory.TAG_CREATION_TIME, + Mp4Directory.TAG_MODIFICATION_TIME, + Mp4MediaDirectory.TAG_CREATION_TIME, + Mp4MediaDirectory.TAG_MODIFICATION_TIME, + QuickTimeMediaDirectory.TAG_CREATION_TIME, + QuickTimeMediaDirectory.TAG_MODIFICATION_TIME -> { + val date = dir.getObject(tag.tagType) + if (date is Date) { + // only consider dates after Epoch time + date.takeIf { it.time > 0 }?.let { + // harmonize date format for further processing on Dart side + dirMap[tagName] = MP4_DATE_FORMAT.format(date) + } + } else { + dirMap[tagName] = tag.description + } + } + Mp4MediaDirectory.TAG_LANGUAGE_CODE -> { + tag.description.takeIf { it != "```" && it != "und" }?.let { dirMap[tagName] = it } + } + else -> dirMap[tagName] = tag.description + } + } } else { dirMap.putAll(tags.map { Pair(it.tagName, it.description) }) } @@ -875,6 +907,10 @@ class MetadataHandler(private val context: Context) : MethodCallHandler { private val LOG_TAG = LogUtils.createTag(MetadataHandler::class.java) const val CHANNEL = "deckers.thibault/aves/metadata" + private val MP4_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXX", Locale.ROOT).apply { + timeZone = TimeZone.getTimeZone("UTC") + } + // catalog metadata & page info private const val KEY_MIME_TYPE = "mimeType" private const val KEY_DATE_MILLIS = "dateMillis" diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MediaMetadataRetrieverHelper.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MediaMetadataRetrieverHelper.kt index 3a160bff7..1d04c0de9 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MediaMetadataRetrieverHelper.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MediaMetadataRetrieverHelper.kt @@ -1,6 +1,5 @@ package deckers.thibault.aves.metadata -import android.content.Context import android.media.MediaFormat import android.media.MediaMetadataRetriever import android.os.Build diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt index a776b8c53..998465677 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt @@ -19,6 +19,9 @@ object Metadata { // "+51.3328-000.7053+113.474/" (Apple) val VIDEO_LOCATION_PATTERN: Pattern = Pattern.compile("([+-][.0-9]+)([+-][.0-9]+).*") + private val VIDEO_DATE_SUBSECOND_PATTERN = Pattern.compile("(\\d{6})(\\.\\d+)") + private val VIDEO_TIMEZONE_PATTERN = Pattern.compile("(Z|[+-]\\d{4})$") + // directory names, as shown when listing all metadata const val DIR_GPS = "GPS" // from metadata-extractor const val DIR_XMP = "XMP" // from metadata-extractor @@ -55,7 +58,7 @@ object Metadata { // optional sub-second var subSecond: String? = null - val subSecondMatcher = Pattern.compile("(\\d{6})(\\.\\d+)").matcher(dateString) + val subSecondMatcher = VIDEO_DATE_SUBSECOND_PATTERN.matcher(dateString) if (subSecondMatcher.find()) { subSecond = subSecondMatcher.group(2)?.substring(1) dateString = subSecondMatcher.replaceAll("$1") @@ -63,7 +66,7 @@ object Metadata { // optional time zone var timeZone: TimeZone? = null - val timeZoneMatcher = Pattern.compile("(Z|[+-]\\d{4})$").matcher(dateString) + val timeZoneMatcher = VIDEO_TIMEZONE_PATTERN.matcher(dateString) if (timeZoneMatcher.find()) { timeZone = TimeZone.getTimeZone("GMT${timeZoneMatcher.group().replace("Z".toRegex(), "")}") dateString = timeZoneMatcher.replaceAll("")