diff --git a/android/app/src/main/java/deckers/thibault/aves/channel/calls/MetadataHandler.java b/android/app/src/main/java/deckers/thibault/aves/channel/calls/MetadataHandler.java index 9217946bf..83741f834 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channel/calls/MetadataHandler.java +++ b/android/app/src/main/java/deckers/thibault/aves/channel/calls/MetadataHandler.java @@ -303,36 +303,44 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { private void getCatalogMetadata(MethodCall call, MethodChannel.Result result) { String mimeType = call.argument("mimeType"); String uri = call.argument("uri"); + String extension = call.argument("extension"); - Map metadataMap = new HashMap<>(getCatalogMetadataByImageMetadataReader(uri, mimeType)); + Map metadataMap = new HashMap<>(getCatalogMetadataByImageMetadataReader(uri, mimeType, extension)); if (isVideo(mimeType)) { metadataMap.putAll(getVideoCatalogMetadataByMediaMetadataRetriever(uri)); } if (metadataMap.isEmpty()) { - result.error("getCatalogMetadata-failure", "failed to get catalog metadata for uri=" + uri, null); + result.error("getCatalogMetadata-failure", "failed to get catalog metadata for uri=" + uri + ", extension=" + extension, null); } else { result.success(metadataMap); } } - private Map getCatalogMetadataByImageMetadataReader(String uri, String mimeType) { + private Map getCatalogMetadataByImageMetadataReader(String uri, String mimeType, String extension) { Map metadataMap = new HashMap<>(); - // as of metadata-extractor 2.14.0, MP2T files are not supported - if (MimeTypes.MP2T.equals(mimeType)) return metadataMap; + // as of metadata-extractor v2.14.0, MP2T/WBMP files are not supported + if (MimeTypes.MP2T.equals(mimeType) || MimeTypes.WBMP.equals(mimeType)) return metadataMap; try (InputStream is = StorageUtils.openInputStream(context, Uri.parse(uri))) { Metadata metadata = ImageMetadataReader.readMetadata(is); // File type for (FileTypeDirectory dir : metadata.getDirectoriesOfType(FileTypeDirectory.class)) { - // the reported `mimeType` (e.g. from Media Store) is sometimes incorrect - // file extension is unreliable + // `metadata-extractor` sometimes detect the the wrong mime type (e.g. `pef` file as `tiff`) + // the content resolver / media store sometimes report the wrong mime type (e.g. `png` file as `jpeg`) // `context.getContentResolver().getType()` sometimes return incorrect value // `MediaMetadataRetriever.setDataSource()` sometimes fail with `status = 0x80000000` if (dir.containsTag(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE)) { - metadataMap.put(KEY_MIME_TYPE, dir.getString(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE)); + String detectedMimeType = dir.getString(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE); + if (detectedMimeType != null && !detectedMimeType.equals(mimeType)) { + // file extension is unreliable, but we use it as a tie breaker + String extensionMimeType = MimeTypes.getMimeTypeForExtension(extension.toLowerCase()); + if (detectedMimeType.equals(extensionMimeType)) { + metadataMap.put(KEY_MIME_TYPE, detectedMimeType); + } + } } } @@ -385,7 +393,7 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { } } } catch (Exception | NoClassDefFoundError e) { - Log.w(LOG_TAG, "failed to get catalog metadata by ImageMetadataReader for uri=" + uri, e); + Log.w(LOG_TAG, "failed to get catalog metadata by ImageMetadataReader for uri=" + uri + ", mimeType=" + mimeType, e); } return metadataMap; } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt index c1e6baf88..33a7e912e 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt @@ -1,27 +1,99 @@ package deckers.thibault.aves.utils +import java.util.* + object MimeTypes { const val IMAGE = "image" + // generic raster const val BMP = "image/bmp" const val GIF = "image/gif" + const val HEIC = "image/heic" + const val HEIF = "image/heif" const val ICO = "image/x-icon" const val JPEG = "image/jpeg" + const val PCX = "image/x-pcx" const val PNG = "image/png" + const val PSD = "image/x-photoshop" // aka "image/vnd.adobe.photoshop" + const val TIFF = "image/tiff" const val WBMP = "image/vnd.wap.wbmp" const val WEBP = "image/webp" - const val HEIC = "image/heic" - const val HEIF = "image/heif" - const val PSD = "image/x-photoshop" // .psd + // raw raster + const val ARW = "image/x-sony-arw" + const val CR2 = "image/x-canon-cr2" + const val CRW = "image/x-canon-crw" + const val DCR = "image/x-kodak-dcr" + const val DNG = "image/x-adobe-dng" + const val ERF = "image/x-epson-erf" + const val K25 = "image/x-kodak-k25" + const val KDC = "image/x-kodak-kdc" + const val MRW = "image/x-minolta-mrw" + const val NEF = "image/x-nikon-nef" + const val NRW = "image/x-nikon-nrw" + const val ORF = "image/x-olympus-orf" + const val PEF = "image/x-pentax-pef" + const val RAF = "image/x-fuji-raf" + const val RAW = "image/x-panasonic-raw" + const val RW2 = "image/x-panasonic-rw2" + const val SR2 = "image/x-sony-sr2" + const val SRF = "image/x-sony-srf" + const val SRW = "image/x-samsung-srw" + const val X3F = "image/x-sigma-x3f" - const val DNG = "image/x-adobe-dng" // .dng - - const val SVG = "image/svg+xml" // .svg + // vector + const val SVG = "image/svg+xml" const val VIDEO = "video" const val AVI = "video/avi" - const val MP2T = "video/mp2t" // .m2ts + const val MOV = "video/quicktime" + const val MP2T = "video/mp2t" const val MP4 = "video/mp4" -} \ No newline at end of file + + @JvmStatic + fun getMimeTypeForExtension(extension: String?): String? = when (extension?.toLowerCase(Locale.ROOT)) { + // generic raster + ".bmp" -> BMP + ".gif" -> GIF + ".heic" -> HEIC + ".heif" -> HEIF + ".ico" -> ICO + ".jpg", ".jpeg", ".jpe" -> JPEG + ".pcx" -> PCX + ".png" -> PNG + ".psd" -> PSD + ".tiff", ".tif" -> TIFF + ".wbmp" -> WBMP + ".webp" -> WEBP + // raw raster + ".arw" -> ARW + ".cr2" -> CR2 + ".crw" -> CRW + ".dcr" -> DCR + ".dng" -> DNG + ".erf" -> ERF + ".k25" -> K25 + ".kdc" -> KDC + ".mrw" -> MRW + ".nef" -> NEF + ".nrw" -> NRW + ".orf" -> ORF + ".pef" -> PEF + ".raf" -> RAF + ".raw" -> RAW + ".rw2" -> RW2 + ".sr2" -> SR2 + ".srf" -> SRF + ".srw" -> SRW + ".x3f" -> X3F + // vector + ".svg" -> SVG + // video + ".avi" -> AVI + ".m2ts" -> MP2T + ".mov", ".qt" -> MOV + ".mp4", ".m4a", ".m4p", ".m4b", ".m4r", ".m4v" -> MP4 + else -> null + } +} diff --git a/lib/services/metadata_service.dart b/lib/services/metadata_service.dart index e3f8d05e3..9e5416177 100644 --- a/lib/services/metadata_service.dart +++ b/lib/services/metadata_service.dart @@ -42,6 +42,7 @@ class MetadataService { final result = await platform.invokeMethod('getCatalogMetadata', { 'mimeType': entry.mimeType, 'uri': entry.uri, + 'extension': entry.extension, }) as Map; result['contentId'] = entry.contentId; return CatalogMetadata.fromMap(result);