avoid double extraction for embedded media
This commit is contained in:
parent
5c8cd81ff7
commit
c95b381fad
9 changed files with 70 additions and 36 deletions
|
@ -20,8 +20,8 @@ import deckers.thibault.aves.metadata.XMP.getSafeStructField
|
|||
import deckers.thibault.aves.metadata.XMPPropName
|
||||
import deckers.thibault.aves.metadata.metadataextractor.Helper
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.model.provider.ContentImageProvider
|
||||
import deckers.thibault.aves.model.provider.ImageProvider
|
||||
import deckers.thibault.aves.model.provider.ImageProviderFactory.getProvider
|
||||
import deckers.thibault.aves.utils.BitmapUtils
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getBytes
|
||||
import deckers.thibault.aves.utils.FileUtils.transferFrom
|
||||
|
@ -341,8 +341,14 @@ class EmbeddedDataHandler(private val context: Context) : MethodCallHandler {
|
|||
"mimeType" to mimeType,
|
||||
)
|
||||
if (isImage(mimeType) || isVideo(mimeType)) {
|
||||
val provider = getProvider(context, uri)
|
||||
if (provider == null) {
|
||||
result.error("copyEmbeddedBytes-provider", "failed to find provider for uri=$uri", null)
|
||||
return
|
||||
}
|
||||
|
||||
ioScope.launch {
|
||||
ContentImageProvider().fetchSingle(context, uri, mimeType, object : ImageProvider.ImageOpCallback {
|
||||
provider.fetchSingle(context, uri, mimeType, object : ImageProvider.ImageOpCallback {
|
||||
override fun onSuccess(fields: FieldMap) {
|
||||
resultFields.putAll(fields)
|
||||
result.success(resultFields)
|
||||
|
|
|
@ -55,7 +55,7 @@ class MediaEditHandler(private val contextWrapper: ContextWrapper) : MethodCallH
|
|||
return
|
||||
}
|
||||
|
||||
val provider = getProvider(uri)
|
||||
val provider = getProvider(contextWrapper, uri)
|
||||
if (provider == null) {
|
||||
result.error("captureFrame-provider", "failed to find provider for uri=$uri", null)
|
||||
return
|
||||
|
|
|
@ -34,7 +34,7 @@ class MediaFetchObjectHandler(private val context: Context) : MethodCallHandler
|
|||
return
|
||||
}
|
||||
|
||||
val provider = getProvider(uri)
|
||||
val provider = getProvider(context, uri)
|
||||
if (provider == null) {
|
||||
result.error("getEntry-provider", "failed to find provider for uri=$uri", null)
|
||||
return
|
||||
|
|
|
@ -62,7 +62,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa
|
|||
return
|
||||
}
|
||||
|
||||
val provider = getProvider(uri)
|
||||
val provider = getProvider(contextWrapper, uri)
|
||||
if (provider == null) {
|
||||
result.error("editOrientation-provider", "failed to find provider for uri=$uri", null)
|
||||
return
|
||||
|
@ -90,7 +90,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa
|
|||
return
|
||||
}
|
||||
|
||||
val provider = getProvider(uri)
|
||||
val provider = getProvider(contextWrapper, uri)
|
||||
if (provider == null) {
|
||||
result.error("editDate-provider", "failed to find provider for uri=$uri", null)
|
||||
return
|
||||
|
@ -117,7 +117,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa
|
|||
return
|
||||
}
|
||||
|
||||
val provider = getProvider(uri)
|
||||
val provider = getProvider(contextWrapper, uri)
|
||||
if (provider == null) {
|
||||
result.error("editMetadata-provider", "failed to find provider for uri=$uri", null)
|
||||
return
|
||||
|
@ -142,7 +142,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa
|
|||
return
|
||||
}
|
||||
|
||||
val provider = getProvider(uri)
|
||||
val provider = getProvider(contextWrapper, uri)
|
||||
if (provider == null) {
|
||||
result.error("removeTrailerVideo-provider", "failed to find provider for uri=$uri", null)
|
||||
return
|
||||
|
@ -168,7 +168,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa
|
|||
return
|
||||
}
|
||||
|
||||
val provider = getProvider(uri)
|
||||
val provider = getProvider(contextWrapper, uri)
|
||||
if (provider == null) {
|
||||
result.error("removeTypes-provider", "failed to find provider for uri=$uri", null)
|
||||
return
|
||||
|
|
|
@ -107,7 +107,7 @@ class ImageOpStreamHandler(private val activity: FragmentActivity, private val a
|
|||
result["skipped"] = true
|
||||
} else {
|
||||
result["success"] = false
|
||||
getProvider(uri)?.let { provider ->
|
||||
getProvider(activity, uri)?.let { provider ->
|
||||
try {
|
||||
provider.delete(activity, uri, path, mimeType)
|
||||
result["success"] = true
|
||||
|
@ -142,7 +142,7 @@ class ImageOpStreamHandler(private val activity: FragmentActivity, private val a
|
|||
|
||||
// assume same provider for all entries
|
||||
val firstEntry = entryMapList.first()
|
||||
val provider = (firstEntry["uri"] as String?)?.let { Uri.parse(it) }?.let { getProvider(it) }
|
||||
val provider = (firstEntry["uri"] as String?)?.let { Uri.parse(it) }?.let { getProvider(activity, it) }
|
||||
if (provider == null) {
|
||||
error("convert-provider", "failed to find provider for entry=$firstEntry", null)
|
||||
return
|
||||
|
@ -231,7 +231,7 @@ class ImageOpStreamHandler(private val activity: FragmentActivity, private val a
|
|||
entriesToNewName[AvesEntry(rawEntry)] = newName
|
||||
}
|
||||
|
||||
val byProvider = entriesToNewName.entries.groupBy { kv -> getProvider(kv.key.uri) }
|
||||
val byProvider = entriesToNewName.entries.groupBy { kv -> getProvider(activity, kv.key.uri) }
|
||||
for ((provider, entryList) in byProvider) {
|
||||
if (provider == null) {
|
||||
error("rename-provider", "failed to find provider for entry=${entryList.firstOrNull()}", null)
|
||||
|
|
|
@ -10,7 +10,9 @@ import java.io.File
|
|||
import java.io.InputStream
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
import java.util.regex.Pattern
|
||||
|
||||
object Metadata {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package deckers.thibault.aves.model.provider
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import java.util.Locale
|
||||
|
||||
class AvesEmbeddedMediaProvider : UnknownContentProvider() {
|
||||
override val reliableProviderMimeType: Boolean
|
||||
get() = true
|
||||
|
||||
companion object {
|
||||
fun provides(context: Context, uri: Uri): Boolean {
|
||||
if (uri.scheme?.lowercase(Locale.ROOT) != ContentResolver.SCHEME_CONTENT) return false
|
||||
return uri.authority == "${context.applicationContext.packageName}.file_provider"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,24 @@
|
|||
package deckers.thibault.aves.model.provider
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import deckers.thibault.aves.utils.StorageUtils
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
object ImageProviderFactory {
|
||||
fun getProvider(uri: Uri): ImageProvider? {
|
||||
fun getProvider(context: Context, uri: Uri): ImageProvider? {
|
||||
return when (uri.scheme?.lowercase(Locale.ROOT)) {
|
||||
ContentResolver.SCHEME_CONTENT -> {
|
||||
if (StorageUtils.isMediaStoreContentUri(uri)) {
|
||||
MediaStoreImageProvider()
|
||||
} else if (AvesEmbeddedMediaProvider.provides(context, uri)) {
|
||||
AvesEmbeddedMediaProvider()
|
||||
} else {
|
||||
ContentImageProvider()
|
||||
UnknownContentProvider()
|
||||
}
|
||||
}
|
||||
|
||||
ContentResolver.SCHEME_FILE -> FileImageProvider()
|
||||
else -> null
|
||||
}
|
||||
|
|
|
@ -13,31 +13,35 @@ import deckers.thibault.aves.utils.LogUtils
|
|||
import deckers.thibault.aves.utils.MimeTypes
|
||||
import deckers.thibault.aves.utils.StorageUtils
|
||||
|
||||
internal class ContentImageProvider : ImageProvider() {
|
||||
open class UnknownContentProvider : ImageProvider() {
|
||||
open val reliableProviderMimeType: Boolean
|
||||
get() = false
|
||||
|
||||
override fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, callback: ImageOpCallback) {
|
||||
// source MIME type may be incorrect, so we get a second opinion if possible
|
||||
var extractorMimeType: String? = null
|
||||
try {
|
||||
val safeUri = Uri.fromFile(Metadata.createPreviewFile(context, uri))
|
||||
StorageUtils.openInputStream(context, safeUri)?.use { input ->
|
||||
// `metadata-extractor` is the most reliable, except for `tiff` (false positives, false negatives)
|
||||
// cf https://github.com/drewnoakes/metadata-extractor/issues/296
|
||||
Helper.readMimeType(input)?.takeIf { it != MimeTypes.TIFF }?.let {
|
||||
extractorMimeType = it
|
||||
if (extractorMimeType != sourceMimeType) {
|
||||
Log.d(LOG_TAG, "source MIME type is $sourceMimeType but extracted MIME type is $extractorMimeType for uri=$uri")
|
||||
var mimeType = sourceMimeType
|
||||
if (sourceMimeType == null || !reliableProviderMimeType) {
|
||||
// source MIME type may be incorrect, so we get a second opinion if possible
|
||||
try {
|
||||
val safeUri = Uri.fromFile(Metadata.createPreviewFile(context, uri))
|
||||
StorageUtils.openInputStream(context, safeUri)?.use { input ->
|
||||
// `metadata-extractor` is the most reliable, except for `tiff` (false positives, false negatives)
|
||||
// cf https://github.com/drewnoakes/metadata-extractor/issues/296
|
||||
Helper.readMimeType(input)?.takeIf { it != MimeTypes.TIFF }?.let {
|
||||
if (it != sourceMimeType) {
|
||||
Log.d(LOG_TAG, "source MIME type is $sourceMimeType but extracted MIME type is $it for uri=$uri")
|
||||
mimeType = it
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
|
||||
} catch (e: NoClassDefFoundError) {
|
||||
Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
|
||||
} catch (e: AssertionError) {
|
||||
Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
|
||||
} catch (e: NoClassDefFoundError) {
|
||||
Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
|
||||
} catch (e: AssertionError) {
|
||||
Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
|
||||
}
|
||||
|
||||
val mimeType = extractorMimeType ?: sourceMimeType
|
||||
val fields: FieldMap = hashMapOf(
|
||||
"origin" to SourceEntry.ORIGIN_UNKNOWN_CONTENT,
|
||||
"uri" to uri.toString(),
|
||||
|
@ -75,6 +79,6 @@ internal class ContentImageProvider : ImageProvider() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = LogUtils.createTag<ContentImageProvider>()
|
||||
private val LOG_TAG = LogUtils.createTag<UnknownContentProvider>()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue