feat: add StrippingResult data class to MetadataStripper
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+21
-16
@@ -33,6 +33,11 @@ import com.vitorpamplona.quartz.utils.Log
|
|||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
data class StrippingResult(
|
||||||
|
val uri: Uri,
|
||||||
|
val stripped: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
class MetadataStripper {
|
class MetadataStripper {
|
||||||
companion object {
|
companion object {
|
||||||
private const val REMUX_BUFFER_SIZE = 8 * 1024 * 1024
|
private const val REMUX_BUFFER_SIZE = 8 * 1024 * 1024
|
||||||
@@ -104,7 +109,7 @@ class MetadataStripper {
|
|||||||
fun stripImageMetadata(
|
fun stripImageMetadata(
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
context: Context,
|
context: Context,
|
||||||
): Uri {
|
): StrippingResult {
|
||||||
return try {
|
return try {
|
||||||
val tempFile = File.createTempFile("stripped_", ".jpg", context.cacheDir)
|
val tempFile = File.createTempFile("stripped_", ".jpg", context.cacheDir)
|
||||||
|
|
||||||
@@ -112,7 +117,7 @@ class MetadataStripper {
|
|||||||
tempFile.outputStream().use { output ->
|
tempFile.outputStream().use { output ->
|
||||||
input.copyTo(output)
|
input.copyTo(output)
|
||||||
}
|
}
|
||||||
} ?: return uri
|
} ?: return StrippingResult(uri, false)
|
||||||
|
|
||||||
val exif = ExifInterface(tempFile.absolutePath)
|
val exif = ExifInterface(tempFile.absolutePath)
|
||||||
for (tag in SENSITIVE_EXIF_TAGS) {
|
for (tag in SENSITIVE_EXIF_TAGS) {
|
||||||
@@ -121,25 +126,25 @@ class MetadataStripper {
|
|||||||
exif.saveAttributes()
|
exif.saveAttributes()
|
||||||
|
|
||||||
Log.d("MetadataStripper", "Stripped EXIF metadata from image")
|
Log.d("MetadataStripper", "Stripped EXIF metadata from image")
|
||||||
tempFile.toUri()
|
StrippingResult(tempFile.toUri(), true)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
Log.d("MetadataStripper", "Failed to strip image metadata: ${e.message}")
|
Log.d("MetadataStripper", "Failed to strip image metadata: ${e.message}")
|
||||||
uri
|
StrippingResult(uri, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stripVideoMetadata(
|
fun stripVideoMetadata(
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
context: Context,
|
context: Context,
|
||||||
): Uri {
|
): StrippingResult {
|
||||||
return try {
|
return try {
|
||||||
val tempInputFile = File.createTempFile("video_input_", ".mp4", context.cacheDir)
|
val tempInputFile = File.createTempFile("video_input_", ".mp4", context.cacheDir)
|
||||||
context.contentResolver.openInputStream(uri)?.use { input ->
|
context.contentResolver.openInputStream(uri)?.use { input ->
|
||||||
tempInputFile.outputStream().use { output ->
|
tempInputFile.outputStream().use { output ->
|
||||||
input.copyTo(output)
|
input.copyTo(output)
|
||||||
}
|
}
|
||||||
} ?: return uri
|
} ?: return StrippingResult(uri, false)
|
||||||
|
|
||||||
val tempOutputFile = File.createTempFile("stripped_video_", ".mp4", context.cacheDir)
|
val tempOutputFile = File.createTempFile("stripped_video_", ".mp4", context.cacheDir)
|
||||||
|
|
||||||
@@ -150,7 +155,7 @@ class MetadataStripper {
|
|||||||
try {
|
try {
|
||||||
extractor.setDataSource(tempInputFile.absolutePath)
|
extractor.setDataSource(tempInputFile.absolutePath)
|
||||||
|
|
||||||
if (extractor.trackCount == 0) return uri
|
if (extractor.trackCount == 0) return StrippingResult(uri, false)
|
||||||
|
|
||||||
// Note: MediaMuxer may still write a creation timestamp and encoder info into
|
// Note: MediaMuxer may still write a creation timestamp and encoder info into
|
||||||
// the new container. This is not controllable via the Android API and is a
|
// the new container. This is not controllable via the Android API and is a
|
||||||
@@ -211,25 +216,25 @@ class MetadataStripper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Log.d("MetadataStripper", "Stripped metadata from video")
|
Log.d("MetadataStripper", "Stripped metadata from video")
|
||||||
tempOutputFile.toUri()
|
StrippingResult(tempOutputFile.toUri(), true)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
Log.d("MetadataStripper", "Failed to strip video metadata: ${e.message}")
|
Log.d("MetadataStripper", "Failed to strip video metadata: ${e.message}")
|
||||||
uri
|
StrippingResult(uri, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stripAudioMetadata(
|
fun stripAudioMetadata(
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
context: Context,
|
context: Context,
|
||||||
): Uri {
|
): StrippingResult {
|
||||||
return try {
|
return try {
|
||||||
val tempInputFile = File.createTempFile("audio_input_", ".tmp", context.cacheDir)
|
val tempInputFile = File.createTempFile("audio_input_", ".tmp", context.cacheDir)
|
||||||
context.contentResolver.openInputStream(uri)?.use { input ->
|
context.contentResolver.openInputStream(uri)?.use { input ->
|
||||||
tempInputFile.outputStream().use { output ->
|
tempInputFile.outputStream().use { output ->
|
||||||
input.copyTo(output)
|
input.copyTo(output)
|
||||||
}
|
}
|
||||||
} ?: return uri
|
} ?: return StrippingResult(uri, false)
|
||||||
|
|
||||||
val extractor = MediaExtractor()
|
val extractor = MediaExtractor()
|
||||||
var muxer: MediaMuxer? = null
|
var muxer: MediaMuxer? = null
|
||||||
@@ -239,7 +244,7 @@ class MetadataStripper {
|
|||||||
try {
|
try {
|
||||||
extractor.setDataSource(tempInputFile.absolutePath)
|
extractor.setDataSource(tempInputFile.absolutePath)
|
||||||
|
|
||||||
if (extractor.trackCount == 0) return uri
|
if (extractor.trackCount == 0) return StrippingResult(uri, false)
|
||||||
|
|
||||||
val format = extractor.getTrackFormat(0)
|
val format = extractor.getTrackFormat(0)
|
||||||
val mime = format.getString(MediaFormat.KEY_MIME) ?: ""
|
val mime = format.getString(MediaFormat.KEY_MIME) ?: ""
|
||||||
@@ -285,11 +290,11 @@ class MetadataStripper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Log.d("MetadataStripper", "Stripped metadata from audio")
|
Log.d("MetadataStripper", "Stripped metadata from audio")
|
||||||
tempOutputFile!!.toUri()
|
StrippingResult(tempOutputFile!!.toUri(), true)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
Log.d("MetadataStripper", "Failed to strip audio metadata: ${e.message}")
|
Log.d("MetadataStripper", "Failed to strip audio metadata: ${e.message}")
|
||||||
uri
|
StrippingResult(uri, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,11 +302,11 @@ class MetadataStripper {
|
|||||||
uri: Uri,
|
uri: Uri,
|
||||||
mimeType: String?,
|
mimeType: String?,
|
||||||
context: Context,
|
context: Context,
|
||||||
): Uri =
|
): StrippingResult =
|
||||||
when {
|
when {
|
||||||
mimeType?.startsWith("image/", ignoreCase = true) == true -> stripImageMetadata(uri, context)
|
mimeType?.startsWith("image/", ignoreCase = true) == true -> stripImageMetadata(uri, context)
|
||||||
mimeType?.startsWith("video/", ignoreCase = true) == true -> stripVideoMetadata(uri, context)
|
mimeType?.startsWith("video/", ignoreCase = true) == true -> stripVideoMetadata(uri, context)
|
||||||
mimeType?.startsWith("audio/", ignoreCase = true) == true -> stripAudioMetadata(uri, context)
|
mimeType?.startsWith("audio/", ignoreCase = true) == true -> stripAudioMetadata(uri, context)
|
||||||
else -> uri
|
else -> StrippingResult(uri, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user