@@ -7,8 +7,30 @@ import android.net.Uri
77import android.provider.DocumentsContract
88import com.musiclibrary.models.*
99import 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
1015
1116object 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+ }
1234
1335 fun getTracks (
1436 contentResolver : ContentResolver ,
@@ -23,8 +45,7 @@ object GetTracksQuery {
2345 MediaStore .Audio .Media .DATA ,
2446 MediaStore .Audio .Media .DATE_ADDED ,
2547 MediaStore .Audio .Media .SIZE ,
26- MediaStore .Audio .Media .ALBUM_ID ,
27- MediaStore .Audio .Media .GENRE
48+ MediaStore .Audio .Media .ALBUM_ID
2849 )
2950
3051 val selection = buildSelection(options)
@@ -53,8 +74,6 @@ object GetTracksQuery {
5374 val dataColumn = c.getColumnIndexOrThrow(MediaStore .Audio .Media .DATA )
5475 val dateAddedColumn = c.getColumnIndexOrThrow(MediaStore .Audio .Media .DATE_ADDED )
5576 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 )
5877
5978 // Jump to the specified start position
6079 val foundAfter = if (options.after == null ) {
@@ -78,50 +97,80 @@ object GetTracksQuery {
7897 var count = 0
7998 val maxItems = options.first.coerceAtMost(1000 ) // Limit the maximum number of queries
8099
100+ val trackPaths = mutableListOf<Pair <String , String >>() // (id, path) pairs
101+
81102 while (foundAfter && count < maxItems) {
82103 try {
83104 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
88105 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()
94106
95107 // Skip invalid data
96108 if (data.isEmpty()) {
97109 continue
98110 }
99111
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
100121 val track = Track (
101122 id = id.toString(),
102123 title = title,
103- artwork = artworkUri.toString(),
104124 artist = artist,
125+ artwork = artworkUri.toString(),
105126 album = album,
106- genre = genre ,
127+ genre = null ,
107128 duration = duration,
108129 url = " file://$data " ,
109130 createdAt = dateAdded * 1000 , // Convert to milliseconds
110131 modifiedAt = dateAdded * 1000 , // Convert to milliseconds
111- fileSize = fileSize
132+ fileSize = fileSize,
133+ lyrics = null
112134 )
113135
114136 tracks.add(track)
137+ trackPaths.add(id.toString() to data)
115138 endCursor = id.toString()
116139 count++
117140 } catch (e: Exception ) {
118- // Continue processing other tracks if a single track fails to parse
119141 continue
120142 }
121143
122144 if (! cursor.moveToNext()) break
123145 }
124146
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+
125174 // Check if there are more data
126175 hasNextPage = ! c.isAfterLast
127176 }
@@ -199,12 +248,6 @@ object GetTracksQuery {
199248 " duration" -> MediaStore .Audio .Media .DURATION
200249 " created_at" -> MediaStore .Audio .Media .DATE_ADDED
201250 " 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-
208251 " track_count" -> MediaStore .Audio .Media .TRACK
209252 else -> throw IllegalArgumentException (" Unsupported SortKey: ${parts[0 ]} " )
210253 }
0 commit comments