music: discard songs w/o volumes
This commit is contained in:
parent
673629dd26
commit
538533bf3f
2 changed files with 26 additions and 29 deletions
|
@ -78,7 +78,7 @@ interface MediaStoreExtractor {
|
||||||
|
|
||||||
fun close()
|
fun close()
|
||||||
|
|
||||||
fun populateFileInfo(rawSong: RawSong)
|
fun populateFileInfo(rawSong: RawSong): Boolean
|
||||||
|
|
||||||
fun populateTags(rawSong: RawSong)
|
fun populateTags(rawSong: RawSong)
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ interface MediaStoreExtractor {
|
||||||
// instead.
|
// instead.
|
||||||
Build.MANUFACTURER.equals("huawei", ignoreCase = true) ||
|
Build.MANUFACTURER.equals("huawei", ignoreCase = true) ||
|
||||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ->
|
Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ->
|
||||||
DataPathInterpreter.Factory(volumeManager)
|
Api24PathInterpreter.Factory(volumeManager)
|
||||||
else -> VolumePathInterpreter.Factory(volumeManager)
|
else -> VolumePathInterpreter.Factory(volumeManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +216,9 @@ private class MediaStoreExtractorImpl(
|
||||||
) {
|
) {
|
||||||
while (query.moveToNext()) {
|
while (query.moveToNext()) {
|
||||||
val rawSong = RawSong()
|
val rawSong = RawSong()
|
||||||
query.populateFileInfo(rawSong)
|
if (!query.populateFileInfo(rawSong)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (cache?.populate(rawSong) == true) {
|
if (cache?.populate(rawSong) == true) {
|
||||||
completeSongs.sendWithTimeout(rawSong)
|
completeSongs.sendWithTimeout(rawSong)
|
||||||
} else {
|
} else {
|
||||||
|
@ -260,13 +262,13 @@ private class MediaStoreExtractorImpl(
|
||||||
|
|
||||||
override fun close() = cursor.close()
|
override fun close() = cursor.close()
|
||||||
|
|
||||||
override fun populateFileInfo(rawSong: RawSong) {
|
override fun populateFileInfo(rawSong: RawSong): Boolean {
|
||||||
rawSong.mediaStoreId = cursor.getLong(idIndex)
|
rawSong.mediaStoreId = cursor.getLong(idIndex)
|
||||||
rawSong.dateAdded = cursor.getLong(dateAddedIndex)
|
rawSong.dateAdded = cursor.getLong(dateAddedIndex)
|
||||||
rawSong.dateModified = cursor.getLong(dateModifiedIndex)
|
rawSong.dateModified = cursor.getLong(dateModifiedIndex)
|
||||||
rawSong.extensionMimeType = cursor.getString(mimeTypeIndex)
|
rawSong.extensionMimeType = cursor.getString(mimeTypeIndex)
|
||||||
rawSong.albumMediaStoreId = cursor.getLong(albumIdIndex)
|
rawSong.albumMediaStoreId = cursor.getLong(albumIdIndex)
|
||||||
pathInterpreter.populate(rawSong)
|
return pathInterpreter.populate(rawSong)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun populateTags(rawSong: RawSong) {
|
override fun populateTags(rawSong: RawSong) {
|
||||||
|
@ -343,19 +345,13 @@ private class MediaStoreExtractorImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface Interpreter {
|
private sealed interface PathInterpreter {
|
||||||
fun populate(rawSong: RawSong)
|
fun populate(rawSong: RawSong): Boolean
|
||||||
|
|
||||||
interface Factory {
|
interface Factory {
|
||||||
val projection: Array<String>
|
val projection: Array<String>
|
||||||
|
|
||||||
fun wrap(cursor: Cursor): Interpreter
|
fun wrap(cursor: Cursor): PathInterpreter
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed interface PathInterpreter : Interpreter {
|
|
||||||
interface Factory : Interpreter.Factory {
|
|
||||||
override fun wrap(cursor: Cursor): PathInterpreter
|
|
||||||
|
|
||||||
fun createSelector(paths: List<Path>): Selector?
|
fun createSelector(paths: List<Path>): Selector?
|
||||||
|
|
||||||
|
@ -363,14 +359,14 @@ private sealed interface PathInterpreter : Interpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DataPathInterpreter(
|
private open class Api24PathInterpreter(
|
||||||
private val cursor: Cursor,
|
private val cursor: Cursor,
|
||||||
private val volumeManager: VolumeManager
|
private val volumeManager: VolumeManager
|
||||||
) : PathInterpreter {
|
) : PathInterpreter {
|
||||||
private val dataIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DATA)
|
private val dataIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DATA)
|
||||||
private val volumes = volumeManager.getVolumes()
|
private val volumes = volumeManager.getVolumes()
|
||||||
|
|
||||||
override fun populate(rawSong: RawSong) {
|
override fun populate(rawSong: RawSong): Boolean {
|
||||||
val data = Components.parseUnix(cursor.getString(dataIndex))
|
val data = Components.parseUnix(cursor.getString(dataIndex))
|
||||||
|
|
||||||
// Find the volume that transforms the DATA column into a relative path. This is
|
// Find the volume that transforms the DATA column into a relative path. This is
|
||||||
|
@ -379,11 +375,11 @@ private class DataPathInterpreter(
|
||||||
val volumePath = volume.components ?: continue
|
val volumePath = volume.components ?: continue
|
||||||
if (volumePath.contains(data)) {
|
if (volumePath.contains(data)) {
|
||||||
rawSong.path = Path(volume, data.depth(volumePath.components.size))
|
rawSong.path = Path(volume, data.depth(volumePath.components.size))
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw IllegalStateException("Could not find volume for path $data (tried: ${volumes.map { it.components }}})")
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(private val volumeManager: VolumeManager) : PathInterpreter.Factory {
|
class Factory(private val volumeManager: VolumeManager) : PathInterpreter.Factory {
|
||||||
|
@ -415,7 +411,7 @@ private class DataPathInterpreter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun wrap(cursor: Cursor): PathInterpreter =
|
override fun wrap(cursor: Cursor): PathInterpreter =
|
||||||
DataPathInterpreter(cursor, volumeManager)
|
Api24PathInterpreter(cursor, volumeManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,20 +425,17 @@ private class VolumePathInterpreter(private val cursor: Cursor, volumeManager: V
|
||||||
cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.RELATIVE_PATH)
|
cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.RELATIVE_PATH)
|
||||||
private val volumes = volumeManager.getVolumes()
|
private val volumes = volumeManager.getVolumes()
|
||||||
|
|
||||||
override fun populate(rawSong: RawSong) {
|
override fun populate(rawSong: RawSong): Boolean {
|
||||||
// Find the StorageVolume whose MediaStore name corresponds to it.
|
// Find the StorageVolume whose MediaStore name corresponds to it.
|
||||||
val volumeName = cursor.getString(volumeIndex)
|
val volumeName = cursor.getString(volumeIndex)
|
||||||
val volume = volumes.find { it.mediaStoreName == volumeName }
|
val volume = volumes.find { it.mediaStoreName == volumeName } ?: return false
|
||||||
if (volume == null) {
|
|
||||||
throw IllegalStateException("Could not find volume for name $volumeName")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relative path does not include file name, must use DISPLAY_NAME and add it
|
// Relative path does not include file name, must use DISPLAY_NAME and add it
|
||||||
// in manually.
|
// in manually.
|
||||||
val relativePath = cursor.getString(relativePathIndex)
|
val relativePath = cursor.getString(relativePathIndex)
|
||||||
val displayName = cursor.getString(displayNameIndex)
|
val displayName = cursor.getString(displayNameIndex)
|
||||||
val components = Components.parseUnix(relativePath).child(displayName)
|
val components = Components.parseUnix(relativePath).child(displayName)
|
||||||
rawSong.path = Path(volume, components)
|
rawSong.path = Path(volume, components)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(private val volumeManager: VolumeManager) : PathInterpreter.Factory {
|
class Factory(private val volumeManager: VolumeManager) : PathInterpreter.Factory {
|
||||||
|
@ -493,9 +486,13 @@ private class VolumePathInterpreter(private val cursor: Cursor, volumeManager: V
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed interface TagInterpreter : Interpreter {
|
private sealed interface TagInterpreter {
|
||||||
interface Factory : Interpreter.Factory {
|
fun populate(rawSong: RawSong)
|
||||||
override fun wrap(cursor: Cursor): TagInterpreter
|
|
||||||
|
interface Factory {
|
||||||
|
val projection: Array<String>
|
||||||
|
|
||||||
|
fun wrap(cursor: Cursor): TagInterpreter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -163,7 +163,7 @@ private fun Fragment.launch(
|
||||||
suspend fun <E> SendChannel<E>.sendWithTimeout(element: E, timeout: Long = 10000) {
|
suspend fun <E> SendChannel<E>.sendWithTimeout(element: E, timeout: Long = 10000) {
|
||||||
try {
|
try {
|
||||||
withTimeout(timeout) { send(element) }
|
withTimeout(timeout) { send(element) }
|
||||||
} catch (e: Exception) {
|
} catch (e: TimeoutCancellationException) {
|
||||||
throw TimeoutException("Timed out sending element $element to channel: $e")
|
throw TimeoutException("Timed out sending element $element to channel: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue