@@ -7,8 +7,30 @@ import android.net.Uri
7
7
import android.provider.DocumentsContract
8
8
import com.musiclibrary.models.*
9
9
import androidx.core.net.toUri
10
+ import java.io.File
11
+ import org.jaudiotagger.audio.AudioFileIO
12
+ import org.jaudiotagger.tag.FieldKey
13
+ import java.util.concurrent.Executors
14
+ import java.util.concurrent.TimeUnit
10
15
11
16
object GetTracksQuery {
17
+ // Create a thread pool
18
+ private val executor = Executors .newFixedThreadPool(4 )
19
+
20
+ private fun getAudioTagInfo (filePath : String ): Pair <String ?, String ?> {
21
+ return try {
22
+ val audioFile = AudioFileIO .read(File (filePath))
23
+ val tag = audioFile.tag
24
+
25
+ // Try to read the genre and lyrics from the tag
26
+ val genre = tag?.getFirst(FieldKey .GENRE )
27
+ val lyrics = tag?.getFirst(FieldKey .LYRICS )
28
+
29
+ Pair (genre, lyrics)
30
+ } catch (e: Exception ) {
31
+ Pair (null , null )
32
+ }
33
+ }
12
34
13
35
fun getTracks (
14
36
contentResolver : ContentResolver ,
@@ -23,8 +45,7 @@ object GetTracksQuery {
23
45
MediaStore .Audio .Media .DATA ,
24
46
MediaStore .Audio .Media .DATE_ADDED ,
25
47
MediaStore .Audio .Media .SIZE ,
26
- MediaStore .Audio .Media .ALBUM_ID ,
27
- MediaStore .Audio .Media .GENRE
48
+ MediaStore .Audio .Media .ALBUM_ID
28
49
)
29
50
30
51
val selection = buildSelection(options)
@@ -53,8 +74,6 @@ object GetTracksQuery {
53
74
val dataColumn = c.getColumnIndexOrThrow(MediaStore .Audio .Media .DATA )
54
75
val dateAddedColumn = c.getColumnIndexOrThrow(MediaStore .Audio .Media .DATE_ADDED )
55
76
val sizeColumn = c.getColumnIndexOrThrow(MediaStore .Audio .Media .SIZE )
56
- val genreColumn = c.getColumnIndexOrThrow(MediaStore .Audio .Media .GENRE )
57
- val albumIdColumn = c.getColumnIndexOrThrow(MediaStore .Audio .Media .ALBUM_ID )
58
77
59
78
// Jump to the specified start position
60
79
val foundAfter = if (options.after == null ) {
@@ -78,50 +97,80 @@ object GetTracksQuery {
78
97
var count = 0
79
98
val maxItems = options.first.coerceAtMost(1000 ) // Limit the maximum number of queries
80
99
100
+ val trackPaths = mutableListOf<Pair <String , String >>() // (id, path) pairs
101
+
81
102
while (foundAfter && count < maxItems) {
82
103
try {
83
104
val id = c.getLong(idColumn)
84
- val title = c.getString(titleColumn) ? : " "
85
- val artist = c.getString(artistColumn) ? : " Unknown Artist"
86
- val album = c.getString(albumColumn) ? : " Unknown Album"
87
- val duration = c.getLong(durationColumn) / 1000.0 // Convert to seconds
88
105
val data = c.getString(dataColumn) ? : " "
89
- val dateAdded = c.getLong(dateAddedColumn)
90
- val fileSize = c.getLong(sizeColumn)
91
- val genre = c.getString(genreColumn) ? : " "
92
- val albumId = c.getLong(albumIdColumn)
93
- val artworkUri: Uri = " content://media/external/audio/media/${id} /albumart" .toUri()
94
106
95
107
// Skip invalid data
96
108
if (data.isEmpty()) {
97
109
continue
98
110
}
99
111
112
+ val title = c.getString(titleColumn) ? : " "
113
+ val artist = c.getString(artistColumn)
114
+ val album = c.getString(albumColumn)
115
+ val duration = c.getLong(durationColumn) / 1000.0 // Convert to seconds
116
+ val dateAdded = c.getLong(dateAddedColumn)
117
+ val fileSize = c.getLong(sizeColumn)
118
+ val artworkUri: Uri = " content://media/external/audio/media/${id} /albumart" .toUri()
119
+
120
+ // Create a Track without lyrics and genre
100
121
val track = Track (
101
122
id = id.toString(),
102
123
title = title,
103
- artwork = artworkUri.toString(),
104
124
artist = artist,
125
+ artwork = artworkUri.toString(),
105
126
album = album,
106
- genre = genre ,
127
+ genre = null ,
107
128
duration = duration,
108
129
url = " file://$data " ,
109
130
createdAt = dateAdded * 1000 , // Convert to milliseconds
110
131
modifiedAt = dateAdded * 1000 , // Convert to milliseconds
111
- fileSize = fileSize
132
+ fileSize = fileSize,
133
+ lyrics = null
112
134
)
113
135
114
136
tracks.add(track)
137
+ trackPaths.add(id.toString() to data)
115
138
endCursor = id.toString()
116
139
count++
117
140
} catch (e: Exception ) {
118
- // Continue processing other tracks if a single track fails to parse
119
141
continue
120
142
}
121
143
122
144
if (! cursor.moveToNext()) break
123
145
}
124
146
147
+ // Batch process tag information loading
148
+ val futures = trackPaths.map { (id, path) ->
149
+ executor.submit<Pair <String , Pair <String ?, String ?>>> {
150
+ val tagInfo = getAudioTagInfo(path)
151
+ Pair (id, tagInfo)
152
+ }
153
+ }
154
+
155
+ // Wait for all tag information to be loaded
156
+ futures.forEach { future ->
157
+ try {
158
+ val result = future.get(5 , TimeUnit .SECONDS )
159
+ val id = result.first
160
+ val (genre, lyrics) = result.second
161
+ val index = tracks.indexOfFirst { it.id == id }
162
+ if (index != - 1 ) {
163
+ val track = tracks[index]
164
+ tracks[index] = track.copy(
165
+ genre = genre,
166
+ lyrics = lyrics
167
+ )
168
+ }
169
+ } catch (e: Exception ) {
170
+ // If the loading times out or fails, keep the original value
171
+ }
172
+ }
173
+
125
174
// Check if there are more data
126
175
hasNextPage = ! c.isAfterLast
127
176
}
@@ -199,12 +248,6 @@ object GetTracksQuery {
199
248
" duration" -> MediaStore .Audio .Media .DURATION
200
249
" created_at" -> MediaStore .Audio .Media .DATE_ADDED
201
250
" modified_at" -> MediaStore .Audio .Media .DATE_MODIFIED
202
- " genre" -> if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
203
- MediaStore .Audio .Media .GENRE
204
- } else {
205
- null
206
- }
207
-
208
251
" track_count" -> MediaStore .Audio .Media .TRACK
209
252
else -> throw IllegalArgumentException (" Unsupported SortKey: ${parts[0 ]} " )
210
253
}
0 commit comments