Skip to content

Commit 262f9ac

Browse files
committed
fix: 修复下载管理界面筛选标签挤压及下载失败/元数据丢失问题
- DownloadManageScreen: Row添加horizontalScroll解决5个FilterChip被挤压 - DownloadWorker: writeTags失败不再导致下载标记为FAILED,文件下载成功即标记COMPLETED - SongMate: writeTags增加null安全检查和通用格式回退,防止封面图下载失败导致NPE - LocalMusicScanner: 更新已有歌曲时检测文件标签存在才覆盖文本元数据,防止扫描器刷空下载元数据 - Download/DownloadWorker: 修复URL中文件扩展名提取逻辑
1 parent a9dab9b commit 262f9ac

5 files changed

Lines changed: 107 additions & 51 deletions

File tree

app/src/main/java/com/ljyh/mei/playback/DownloadWorker.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ class DownloadWorker(
123123
updateTask(db, songId, DownloadStatus.DOWNLOADING, 0)
124124

125125
val suffix = task.fileType.ifBlank {
126-
task.url.substringBeforeLast("?").substringAfterLast(".")
126+
val pathWithoutQuery = task.url.substringBefore("?")
127+
val lastSegment = pathWithoutQuery.substringAfterLast("/")
128+
lastSegment.substringAfterLast(".", "")
127129
}
128130
if (suffix.isBlank()) {
129131
failedCount++
@@ -139,10 +141,14 @@ class DownloadWorker(
139141
updateTask(db, songId, DownloadStatus.DOWNLOADING, progress)
140142
}
141143
if (success && songFile.exists()) {
142-
SongMate.writeTags(
143-
task.songTitle, task.songArtist, task.songAlbum,
144-
task.songCover, songFile.absolutePath
145-
)
144+
try {
145+
SongMate.writeTags(
146+
task.songTitle, task.songArtist, task.songAlbum,
147+
task.songCover, songFile.absolutePath
148+
)
149+
} catch (e: Exception) {
150+
Timber.e(e, "writeTags failed for ${task.songTitle}")
151+
}
146152
db.songDao().insertSong(
147153
Song(
148154
id = songId,

app/src/main/java/com/ljyh/mei/ui/screen/setting/DownloadManageScreen.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.ljyh.mei.ui.screen.setting
22

33
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.horizontalScroll
45
import androidx.compose.foundation.layout.Arrangement
6+
import androidx.compose.foundation.rememberScrollState
57
import androidx.compose.foundation.layout.Box
68
import androidx.compose.foundation.layout.Column
79
import androidx.compose.foundation.layout.Row
@@ -149,6 +151,7 @@ fun DownloadManageScreen(
149151
Row(
150152
modifier = Modifier
151153
.fillMaxWidth()
154+
.horizontalScroll(rememberScrollState())
152155
.padding(horizontal = 12.dp, vertical = 8.dp),
153156
horizontalArrangement = Arrangement.spacedBy(8.dp)
154157
) {

app/src/main/java/com/ljyh/mei/utils/Download.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ object DownloadManager {
6060
url = info.url ?: "",
6161
fileName = "",
6262
fileType = info.fileType.ifBlank {
63-
info.url?.substringBeforeLast("?")?.substringAfterLast(".") ?: ""
63+
val pathWithoutQuery = (info.url ?: "").substringBefore("?")
64+
val lastSegment = pathWithoutQuery.substringAfterLast("/")
65+
lastSegment.substringAfterLast(".", "")
6466
},
6567
status = DownloadStatus.PENDING,
6668
progress = 0,

app/src/main/java/com/ljyh/mei/utils/LocalMusicScanner.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,16 @@ class LocalMusicScanner(
151151
if (existing != null) {
152152
val updatedSong = createSongFromFile(file, folderPath)
153153
if (updatedSong != null) {
154+
val fileHasTags = try {
155+
val tag = org.jaudiotagger.audio.AudioFileIO.read(file).tag
156+
tag?.getFirst(FieldKey.TITLE)?.isNotBlank() == true
157+
} catch (_: Exception) { false }
154158
songRepository.updateMetadata(
155159
id = existing.id,
156-
title = updatedSong.title,
157-
artist = updatedSong.artist,
158-
album = updatedSong.album,
159-
cover = updatedSong.cover,
160+
title = if (fileHasTags) updatedSong.title else existing.title,
161+
artist = if (fileHasTags) updatedSong.artist else existing.artist,
162+
album = if (fileHasTags) updatedSong.album else existing.album,
163+
cover = if (fileHasTags && updatedSong.cover.isNotEmpty()) updatedSong.cover else existing.cover,
160164
duration = updatedSong.duration,
161165
path = updatedSong.path,
162166
fileHash = updatedSong.fileHash,

app/src/main/java/com/ljyh/mei/utils/SongMate.kt

Lines changed: 82 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.jaudiotagger.tag.FieldKey
1010
import org.jaudiotagger.tag.flac.FlacTag
1111
import org.jaudiotagger.tag.id3.valuepair.ImageFormats
1212
import org.jaudiotagger.tag.images.ArtworkFactory
13+
import timber.log.Timber
1314
import java.io.File
1415

1516
object SongMate {
@@ -113,51 +114,91 @@ object SongMate {
113114
coverUrl: String,
114115
filePath: String
115116
) {
116-
val suffix = filePath.substringAfterLast(".")
117-
when (suffix.lowercase()) {
118-
"flac" -> {
119-
val file = File(filePath)
120-
val audioFile: AudioFile = AudioFileIO.read(file)
121-
val tag = audioFile.tagOrCreateAndSetDefault as FlacTag
122-
tag.setField(FieldKey.TITLE, title)
123-
tag.setField(FieldKey.ARTIST, artist)
124-
tag.setField(FieldKey.ALBUM, album)
125-
tag.setField(FieldKey.ALBUM_ARTIST, artist)
126-
if (coverUrl.isNotBlank()) {
127-
tag.setField(
128-
tag.createArtworkField(
129-
downloadImageBytes(coverUrl),
130-
6,
131-
ImageFormats.MIME_TYPE_JPEG,
132-
"Image",
133-
1400,
134-
1400,
135-
24,
136-
0
137-
)
138-
)
117+
val suffix = filePath.substringAfterLast(".").lowercase()
118+
try {
119+
when (suffix) {
120+
"flac" -> writeFlacTags(title, artist, album, coverUrl, filePath)
121+
"mp3" -> writeMp3Tags(title, artist, album, coverUrl, filePath)
122+
else -> {
123+
try {
124+
val file = File(filePath)
125+
val audioFile: AudioFile = AudioFileIO.read(file)
126+
val tag = audioFile.tagOrCreateAndSetDefault
127+
tag.setField(FieldKey.TITLE, title)
128+
tag.setField(FieldKey.ARTIST, artist)
129+
tag.setField(FieldKey.ALBUM, album)
130+
tag.setField(FieldKey.ALBUM_ARTIST, artist)
131+
val imageBytes = downloadImageBytes(coverUrl)
132+
if (coverUrl.isNotBlank() && imageBytes != null) {
133+
tag.deleteArtworkField()
134+
val artwork = ArtworkFactory.getNew()
135+
artwork.mimeType = "image/jpeg"
136+
artwork.binaryData = imageBytes
137+
artwork.pictureType = 6
138+
artwork.description = "Cover"
139+
tag.setField(artwork)
140+
}
141+
audioFile.commit()
142+
} catch (e: Exception) {
143+
Timber.e(e, "writeTags fallback failed for $filePath")
144+
}
139145
}
140-
audioFile.commit()
141146
}
142-
"mp3" -> {
143-
val file = File(filePath)
144-
val audioFile: AudioFile = AudioFileIO.read(file)
145-
val tag = audioFile.tagOrCreateAndSetDefault
146-
tag.setField(FieldKey.TITLE, title)
147-
tag.setField(FieldKey.ARTIST, artist)
148-
tag.setField(FieldKey.ALBUM, album)
149-
tag.setField(FieldKey.ALBUM_ARTIST, artist)
150-
if (coverUrl.isNotBlank()) {
151-
val artwork = ArtworkFactory.getNew()
152-
artwork.mimeType = "image/jpeg"
153-
artwork.binaryData = downloadImageBytes(coverUrl)
154-
artwork.pictureType = 6
155-
artwork.description = "Cover"
156-
tag.setField(artwork)
157-
}
158-
audioFile.commit()
147+
} catch (e: Exception) {
148+
Timber.e(e, "writeTags error for $filePath")
149+
}
150+
}
151+
152+
private suspend fun writeFlacTags(
153+
title: String, artist: String, album: String,
154+
coverUrl: String, filePath: String
155+
) {
156+
val file = File(filePath)
157+
val audioFile: AudioFile = AudioFileIO.read(file)
158+
val tag = audioFile.tagOrCreateAndSetDefault as FlacTag
159+
tag.setField(FieldKey.TITLE, title)
160+
tag.setField(FieldKey.ARTIST, artist)
161+
tag.setField(FieldKey.ALBUM, album)
162+
tag.setField(FieldKey.ALBUM_ARTIST, artist)
163+
if (coverUrl.isNotBlank()) {
164+
val imageBytes = downloadImageBytes(coverUrl)
165+
if (imageBytes != null) {
166+
tag.setField(
167+
tag.createArtworkField(
168+
imageBytes,
169+
6, ImageFormats.MIME_TYPE_JPEG, "Image",
170+
1400, 1400, 24, 0
171+
)
172+
)
173+
}
174+
}
175+
audioFile.commit()
176+
}
177+
178+
private suspend fun writeMp3Tags(
179+
title: String, artist: String, album: String,
180+
coverUrl: String, filePath: String
181+
) {
182+
val file = File(filePath)
183+
val audioFile: AudioFile = AudioFileIO.read(file)
184+
val tag = audioFile.tagOrCreateAndSetDefault
185+
tag.setField(FieldKey.TITLE, title)
186+
tag.setField(FieldKey.ARTIST, artist)
187+
tag.setField(FieldKey.ALBUM, album)
188+
tag.setField(FieldKey.ALBUM_ARTIST, artist)
189+
if (coverUrl.isNotBlank()) {
190+
val imageBytes = downloadImageBytes(coverUrl)
191+
if (imageBytes != null) {
192+
tag.deleteArtworkField()
193+
val artwork = ArtworkFactory.getNew()
194+
artwork.mimeType = "image/jpeg"
195+
artwork.binaryData = imageBytes
196+
artwork.pictureType = 6
197+
artwork.description = "Cover"
198+
tag.setField(artwork)
159199
}
160200
}
201+
audioFile.commit()
161202
}
162203

163204
fun writeLyric(filePath: String, lyric: String): Boolean {

0 commit comments

Comments
 (0)