fix: Fix corrupting of files when writing tags to unsupported files

This commit is contained in:
Prathamesh More 2023-03-18 14:28:53 +05:30
parent 3d7e89e270
commit 25fcb84ea8
4 changed files with 171 additions and 146 deletions

View file

@ -154,7 +154,7 @@ dependencies {
implementation "io.insert-koin:koin-core:$koin_version" implementation "io.insert-koin:koin-core:$koin_version"
implementation "io.insert-koin:koin-android:$koin_version" implementation "io.insert-koin:koin-android:$koin_version"
def glide_version = '4.15.0' def glide_version = '4.15.1'
implementation "com.github.bumptech.glide:glide:$glide_version" implementation "com.github.bumptech.glide:glide:$glide_version"
ksp "com.github.bumptech.glide:ksp:$glide_version" ksp "com.github.bumptech.glide:ksp:$glide_version"
implementation "com.github.bumptech.glide:okhttp3-integration:$glide_version" implementation "com.github.bumptech.glide:okhttp3-integration:$glide_version"

View file

@ -46,6 +46,7 @@ import code.name.monkey.retromusic.model.ArtworkInfo
import code.name.monkey.retromusic.model.AudioTagInfo import code.name.monkey.retromusic.model.AudioTagInfo
import code.name.monkey.retromusic.repository.Repository import code.name.monkey.retromusic.repository.Repository
import code.name.monkey.retromusic.util.logD import code.name.monkey.retromusic.util.logD
import code.name.monkey.retromusic.util.logE
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -98,7 +99,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -107,7 +109,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TITLE) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TITLE)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -115,7 +118,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.COMPOSER) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.COMPOSER)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -124,7 +128,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -133,7 +138,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -142,7 +148,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -151,7 +158,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -160,7 +168,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.YEAR) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.YEAR)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -169,7 +178,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TRACK) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TRACK)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -178,7 +188,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.DISC_NO) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.DISC_NO)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -187,7 +198,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.LYRICS) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.LYRICS)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -205,7 +217,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
) )
} }
return null return null
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
return null return null
} }
} }
@ -356,9 +369,12 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
artworkInfo artworkInfo
) )
) )
val pendingIntent = MediaStore.createWriteRequest(contentResolver, getSongUris())
launcher.launch(IntentSenderRequest.Builder(pendingIntent).build()) if (cacheFiles.isNotEmpty()) {
val pendingIntent =
MediaStore.createWriteRequest(contentResolver, getSongUris())
launcher.launch(IntentSenderRequest.Builder(pendingIntent).build())
}
} else { } else {
TagWriter.writeTagsToFiles( TagWriter.writeTagsToFiles(
this@AbsTagEditorActivity, AudioTagInfo( this@AbsTagEditorActivity, AudioTagInfo(
@ -396,9 +412,14 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
} }
} }
private lateinit var audioFile: AudioFile
private fun getAudioFile(path: String): AudioFile { private fun getAudioFile(path: String): AudioFile {
return try { return try {
AudioFileIO.read(File(path)) if (!this::audioFile.isInitialized) {
audioFile = AudioFileIO.read(File(path))
}
audioFile
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Could not read audio file $path", e) Log.e(TAG, "Could not read audio file $path", e)
AudioFile() AudioFile()

View file

@ -7,6 +7,7 @@ import android.media.MediaScannerConnection
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.showToast import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener
import code.name.monkey.retromusic.model.AudioTagInfo import code.name.monkey.retromusic.model.AudioTagInfo
@ -20,6 +21,7 @@ import org.jaudiotagger.audio.exceptions.CannotReadException
import org.jaudiotagger.audio.exceptions.CannotWriteException import org.jaudiotagger.audio.exceptions.CannotWriteException
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
import org.jaudiotagger.tag.FieldDataInvalidException
import org.jaudiotagger.tag.TagException import org.jaudiotagger.tag.TagException
import org.jaudiotagger.tag.images.AndroidArtwork import org.jaudiotagger.tag.images.AndroidArtwork
import org.jaudiotagger.tag.images.Artwork import org.jaudiotagger.tag.images.Artwork
@ -31,9 +33,9 @@ class TagWriter {
companion object { companion object {
suspend fun scan(context: Context, toBeScanned: List<String?>?) { suspend fun scan(context: Context, toBeScanned: List<String?>?) {
if (toBeScanned == null || toBeScanned.isEmpty()) { if (toBeScanned.isNullOrEmpty()) {
Log.i("scan", "scan: Empty") Log.i("scan", "scan: Empty")
context.showToast( "Scan file from folder") context.showToast("Scan file from folder")
return return
} }
MediaScannerConnection.scanFile( MediaScannerConnection.scanFile(
@ -50,150 +52,151 @@ class TagWriter {
suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) { suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
runCatching { var artwork: Artwork? = null
var artwork: Artwork? = null var albumArtFile: File? = null
var albumArtFile: File? = null if (info.artworkInfo?.artwork != null) {
if (info.artworkInfo?.artwork != null) { try {
try { albumArtFile = createAlbumArtFile(context).canonicalFile
albumArtFile = createAlbumArtFile(context).canonicalFile info.artworkInfo.artwork.compress(
info.artworkInfo.artwork.compress( Bitmap.CompressFormat.JPEG,
Bitmap.CompressFormat.JPEG, 100,
100, albumArtFile.outputStream()
albumArtFile.outputStream() )
) artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
artwork = AndroidArtwork.createArtworkFromFile(albumArtFile) } catch (e: IOException) {
} catch (e: IOException) { e.printStackTrace()
e.printStackTrace()
}
} }
var wroteArtwork = false
var deletedArtwork = false
for (filePath in info.filePaths!!) {
try {
val audioFile = AudioFileIO.read(File(filePath))
val tag = audioFile.tagOrCreateAndSetDefault
if (info.fieldKeyValueMap != null) {
for ((key, value) in info.fieldKeyValueMap) {
try {
tag.setField(key, value)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
if (info.artworkInfo != null) {
if (info.artworkInfo.artwork == null) {
tag.deleteArtworkField()
deletedArtwork = true
} else if (artwork != null) {
tag.deleteArtworkField()
tag.setField(artwork)
wroteArtwork = true
}
}
audioFile.commit()
} catch (e: CannotReadException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: CannotWriteException) {
e.printStackTrace()
} catch (e: TagException) {
e.printStackTrace()
} catch (e: ReadOnlyFileException) {
e.printStackTrace()
} catch (e: InvalidAudioFrameException) {
e.printStackTrace()
}
}
if (wroteArtwork) {
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
} else if (deletedArtwork) {
deleteAlbumArt(context, info.artworkInfo!!.albumId)
}
scan(context, info.filePaths)
}.onFailure {
it.printStackTrace()
} }
var wroteArtwork = false
var deletedArtwork = false
for (filePath in info.filePaths!!) {
try {
val audioFile = AudioFileIO.read(File(filePath))
val tag = audioFile.tagOrCreateAndSetDefault
if (info.fieldKeyValueMap != null) {
for ((key, value) in info.fieldKeyValueMap) {
try {
tag.setField(key, value)
} catch (e: FieldDataInvalidException) {
withContext(Dispatchers.Main) {
context.showToast(R.string.could_not_write_tags_to_file)
}
return@withContext listOf<File>()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
if (info.artworkInfo != null) {
if (info.artworkInfo.artwork == null) {
tag.deleteArtworkField()
deletedArtwork = true
} else if (artwork != null) {
tag.deleteArtworkField()
tag.setField(artwork)
wroteArtwork = true
}
}
audioFile.commit()
} catch (e: CannotReadException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: CannotWriteException) {
e.printStackTrace()
} catch (e: TagException) {
e.printStackTrace()
} catch (e: ReadOnlyFileException) {
e.printStackTrace()
} catch (e: InvalidAudioFrameException) {
e.printStackTrace()
}
}
if (wroteArtwork) {
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
} else if (deletedArtwork) {
deleteAlbumArt(context, info.artworkInfo!!.albumId)
}
scan(context, info.filePaths)
} }
} }
@RequiresApi(Build.VERSION_CODES.R) @RequiresApi(Build.VERSION_CODES.R)
suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> = suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val cacheFiles = mutableListOf<File>() val cacheFiles = mutableListOf<File>()
runCatching { var artwork: Artwork? = null
var artwork: Artwork? = null var albumArtFile: File? = null
var albumArtFile: File? = null if (info.artworkInfo?.artwork != null) {
if (info.artworkInfo?.artwork != null) { try {
try { albumArtFile = createAlbumArtFile(context).canonicalFile
albumArtFile = createAlbumArtFile(context).canonicalFile info.artworkInfo.artwork.compress(
info.artworkInfo.artwork.compress( Bitmap.CompressFormat.JPEG,
Bitmap.CompressFormat.JPEG, 100,
100, albumArtFile.outputStream()
albumArtFile.outputStream() )
) artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
artwork = AndroidArtwork.createArtworkFromFile(albumArtFile) } catch (e: IOException) {
} catch (e: IOException) { e.printStackTrace()
e.printStackTrace()
}
} }
var wroteArtwork = false }
var deletedArtwork = false var wroteArtwork = false
for (filePath in info.filePaths!!) { var deletedArtwork = false
try { for (filePath in info.filePaths!!) {
val originFile = File(filePath) try {
val cacheFile = File(context.cacheDir, originFile.name) val originFile = File(filePath)
cacheFiles.add(cacheFile) val cacheFile = File(context.cacheDir, originFile.name)
originFile.inputStream().use { input -> cacheFiles.add(cacheFile)
cacheFile.outputStream().use { output -> originFile.inputStream().use { input ->
input.copyTo(output) cacheFile.outputStream().use { output ->
} input.copyTo(output)
} }
val audioFile = AudioFileIO.read(cacheFile) }
val tag = audioFile.tagOrCreateAndSetDefault val audioFile = AudioFileIO.read(cacheFile)
if (info.fieldKeyValueMap != null) { val tag = audioFile.tagOrCreateAndSetDefault
for ((key, value) in info.fieldKeyValueMap) { if (info.fieldKeyValueMap != null) {
try { for ((key, value) in info.fieldKeyValueMap) {
tag.setField(key, value) try {
} catch (e: Exception) { tag.setField(key, value)
e.printStackTrace() } catch (e: FieldDataInvalidException) {
withContext(Dispatchers.Main) {
context.showToast(R.string.could_not_write_tags_to_file)
} }
return@withContext listOf<File>()
} catch (e: Exception) {
e.printStackTrace()
} }
} }
if (info.artworkInfo != null) {
if (info.artworkInfo.artwork == null) {
tag.deleteArtworkField()
deletedArtwork = true
} else if (artwork != null) {
tag.deleteArtworkField()
tag.setField(artwork)
wroteArtwork = true
}
}
audioFile.commit()
} catch (e: CannotReadException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: CannotWriteException) {
e.printStackTrace()
} catch (e: TagException) {
e.printStackTrace()
} catch (e: ReadOnlyFileException) {
e.printStackTrace()
} catch (e: InvalidAudioFrameException) {
e.printStackTrace()
} }
if (info.artworkInfo != null) {
if (info.artworkInfo.artwork == null) {
tag.deleteArtworkField()
deletedArtwork = true
} else if (artwork != null) {
tag.deleteArtworkField()
tag.setField(artwork)
wroteArtwork = true
}
}
audioFile.commit()
} catch (e: CannotReadException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: CannotWriteException) {
e.printStackTrace()
} catch (e: TagException) {
e.printStackTrace()
} catch (e: ReadOnlyFileException) {
e.printStackTrace()
} catch (e: InvalidAudioFrameException) {
e.printStackTrace()
} }
if (wroteArtwork) { }
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path) if (wroteArtwork) {
} else if (deletedArtwork) { insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
deleteAlbumArt(context, info.artworkInfo!!.albumId) } else if (deletedArtwork) {
} deleteAlbumArt(context, info.artworkInfo!!.albumId)
}.onFailure {
it.printStackTrace()
} }
cacheFiles cacheFiles
} }

View file

@ -562,4 +562,5 @@
<string name="you_have_to_select_at_least_one_category">You have to select at least one category.</string> <string name="you_have_to_select_at_least_one_category">You have to select at least one category.</string>
<string name="you_will_be_forwarded_to_the_issue_tracker_website">You will be forwarded to the issue tracker website.</string> <string name="you_will_be_forwarded_to_the_issue_tracker_website">You will be forwarded to the issue tracker website.</string>
<string name="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string> <string name="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string>
<string name="could_not_write_tags_to_file">Could not write tags to the music file!</string>
</resources> </resources>