music: make field utils functions
Make some field utils functions, as they do work.
This commit is contained in:
parent
969c0c69b7
commit
7833ec4460
6 changed files with 81 additions and 112 deletions
|
@ -5,7 +5,7 @@
|
||||||
#### What's New
|
#### What's New
|
||||||
- Added option to ignore `MediaStore` tags, allowing more correct metadata
|
- Added option to ignore `MediaStore` tags, allowing more correct metadata
|
||||||
at the cost of longer loading times
|
at the cost of longer loading times
|
||||||
- Added support for sort tags [#174, dependent on this feature]
|
- Added support for sort tags [#172, dependent on this feature]
|
||||||
- Added Last Added sorting
|
- Added Last Added sorting
|
||||||
|
|
||||||
## 2.5.0
|
## 2.5.0
|
||||||
|
|
|
@ -132,7 +132,7 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
||||||
|
|
||||||
// ViewPager2 will nominally consume window insets, which will then break the window
|
// ViewPager2 will nominally consume window insets, which will then break the window
|
||||||
// insets applied to the indexing view before API 30. Fix this by overriding the
|
// insets applied to the indexing view before API 30. Fix this by overriding the
|
||||||
// callback with a no-op listener.
|
// callback with a non-consuming listener.
|
||||||
setOnApplyWindowInsetsListener { _, insets -> insets }
|
setOnApplyWindowInsetsListener { _, insets -> insets }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,8 +343,6 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleNavigation(item: Music?) {
|
private fun handleNavigation(item: Music?) {
|
||||||
// Note: You will want to add a post call to this if you want to re-introduce a collapsing
|
|
||||||
// toolbar.
|
|
||||||
when (item) {
|
when (item) {
|
||||||
is Song ->
|
is Song ->
|
||||||
findNavController().navigate(HomeFragmentDirections.actionShowAlbum(item.album.id))
|
findNavController().navigate(HomeFragmentDirections.actionShowAlbum(item.album.id))
|
||||||
|
|
|
@ -37,14 +37,11 @@ sealed class Music : Item() {
|
||||||
abstract val rawSortName: String?
|
abstract val rawSortName: String?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of this item used for sorting. This will first use the sort tag for the item,
|
* The name of this item used for sorting.This should not be used outside of sorting and
|
||||||
* followed by the name without a preceding article (The/A/An). In the case that the item has no
|
* fast-scrolling.
|
||||||
* name, this returns null.
|
|
||||||
*
|
|
||||||
* This should not be used outside of sorting and fast-scrolling.
|
|
||||||
*/
|
*/
|
||||||
val sortName: String?
|
val sortName: String?
|
||||||
get() = rawSortName ?: rawName?.withoutArticle
|
get() = rawSortName ?: rawName?.parseSortName()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a name from it's raw form to a form suitable to be shown in a ui. Ex. "unknown" would
|
* Resolve a name from it's raw form to a form suitable to be shown in a ui. Ex. "unknown" would
|
||||||
|
@ -275,8 +272,9 @@ data class Genre(override val rawName: String?, override val songs: List<Song>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort tags don't make sense on genres
|
||||||
override val rawSortName: String?
|
override val rawSortName: String?
|
||||||
get() = null
|
get() = rawName
|
||||||
|
|
||||||
override val id: Long
|
override val id: Long
|
||||||
get() = (rawName ?: MediaStore.UNKNOWN_STRING).hashCode().toLong()
|
get() = (rawName ?: MediaStore.UNKNOWN_STRING).hashCode().toLong()
|
||||||
|
|
|
@ -55,42 +55,30 @@ val Long.audioUri: Uri
|
||||||
val Long.albumCoverUri: Uri
|
val Long.albumCoverUri: Uri
|
||||||
get() = ContentUris.withAppendedId(EXTERNAL_ALBUM_ART_URI, this)
|
get() = ContentUris.withAppendedId(EXTERNAL_ALBUM_ART_URI, this)
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse out the number field from a field assumed to be NN, where NN is a track number. This is
|
|
||||||
* most commonly found on vorbis comments. Values of zero will be ignored under the assumption that
|
|
||||||
* they are invalid.
|
|
||||||
*/
|
|
||||||
val String.plainTrackNo: Int?
|
|
||||||
get() = toIntOrNull()?.nonZeroOrNull()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse out the track number field as if the given Int is formatted as DTTT, where D Is the disc
|
* Parse out the track number field as if the given Int is formatted as DTTT, where D Is the disc
|
||||||
* and T is the track number. Values of zero will be ignored under the assumption that they are
|
* and T is the track number. Values of zero will be ignored under the assumption that they are
|
||||||
* invalid.
|
* invalid.
|
||||||
*/
|
*/
|
||||||
val Int.packedTrackNo: Int?
|
fun Int.unpackTrackNo() = mod(1000).nonZeroOrNull()
|
||||||
get() = mod(1000).nonZeroOrNull()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse out the disc number field as if the given Int is formatted as DTTT, where D Is the disc and
|
* Parse out the disc number field as if the given Int is formatted as DTTT, where D Is the disc and
|
||||||
* T is the track number. Values of zero will be ignored under the assumption that they are invalid.
|
* T is the track number. Values of zero will be ignored under the assumption that they are invalid.
|
||||||
*/
|
*/
|
||||||
val Int.packedDiscNo: Int?
|
fun Int.unpackDiscNo() = div(1000).nonZeroOrNull()
|
||||||
get() = div(1000).nonZeroOrNull()
|
|
||||||
|
/**
|
||||||
|
* Parse out a plain number from a string. Values of 0 will be ignored under the assumption that
|
||||||
|
* they are invalid.
|
||||||
|
*/
|
||||||
|
fun String.parseNum() = toIntOrNull()?.nonZeroOrNull()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse out the number field from an NN/TT string that is typically found in DISC_NUMBER and
|
* Parse out the number field from an NN/TT string that is typically found in DISC_NUMBER and
|
||||||
* CD_TRACK_NUMBER. Values of zero will be ignored under the assumption that they are invalid.
|
* CD_TRACK_NUMBER. Values of zero will be ignored under the assumption that they are invalid.
|
||||||
*/
|
*/
|
||||||
val String.trackDiscNo: Int?
|
fun String.parsePositionNum() = split('/', limit = 2)[0].toIntOrNull()?.nonZeroOrNull()
|
||||||
get() = split('/', limit = 2)[0].toIntOrNull()?.nonZeroOrNull()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse out a plain year from a string. Values of 0 will be ignored under the assumption that they
|
|
||||||
* are invalid.
|
|
||||||
*/
|
|
||||||
val String.year: Int?
|
|
||||||
get() = toIntOrNull()?.nonZeroOrNull()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse out the year field from a (presumably) ISO-8601-like date. This differs across tag formats
|
* Parse out the year field from a (presumably) ISO-8601-like date. This differs across tag formats
|
||||||
|
@ -98,8 +86,7 @@ val String.year: Int?
|
||||||
* (...) and thus we can parse the year out by splitting at the first -. Values of 0 will be ignored
|
* (...) and thus we can parse the year out by splitting at the first -. Values of 0 will be ignored
|
||||||
* under the assumption that they are invalid.
|
* under the assumption that they are invalid.
|
||||||
*/
|
*/
|
||||||
val String.iso8601year: Int?
|
fun String.parseIso8601Year() = split('-', limit = 2)[0].toIntOrNull()?.nonZeroOrNull()
|
||||||
get() = split('-', limit = 2)[0].toIntOrNull()?.nonZeroOrNull()
|
|
||||||
|
|
||||||
private fun Int.nonZeroOrNull() = if (this > 0) this else null
|
private fun Int.nonZeroOrNull() = if (this > 0) this else null
|
||||||
|
|
||||||
|
@ -108,29 +95,19 @@ private fun Int.nonZeroOrNull() = if (this > 0) this else null
|
||||||
* anglo-centric, but it's also a bit of an expected feature in music players, so we implement it
|
* anglo-centric, but it's also a bit of an expected feature in music players, so we implement it
|
||||||
* anyway.
|
* anyway.
|
||||||
*/
|
*/
|
||||||
val String.withoutArticle: String
|
fun String.parseSortName() =
|
||||||
get() {
|
when {
|
||||||
if (length > 5 && startsWith("the ", ignoreCase = true)) {
|
length > 5 && startsWith("the ", ignoreCase = true) -> substring(4)
|
||||||
return slice(4..lastIndex)
|
length > 4 && startsWith("an ", ignoreCase = true) -> substring(3)
|
||||||
}
|
length > 3 && startsWith("a ", ignoreCase = true) -> substring(2)
|
||||||
|
else -> this
|
||||||
if (length > 4 && startsWith("an ", ignoreCase = true)) {
|
|
||||||
return slice(3..lastIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length > 3 && startsWith("a ", ignoreCase = true)) {
|
|
||||||
return slice(2..lastIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes the genre name from an ID3(v2) constant. See [GENRE_TABLE] for the genre constant map
|
* Decodes the genre name from an ID3(v2) constant. See [GENRE_TABLE] for the genre constant map
|
||||||
* that Auxio uses.
|
* that Auxio uses.
|
||||||
*/
|
*/
|
||||||
val String.id3GenreName: String
|
fun String.parseId3GenreName() = parseId3v1Genre() ?: parseId3v2Genre() ?: this
|
||||||
get() = parseId3v1Genre() ?: parseId3v2Genre() ?: this
|
|
||||||
|
|
||||||
private fun String.parseId3v1Genre(): String? =
|
private fun String.parseId3v1Genre(): String? =
|
||||||
when {
|
when {
|
||||||
|
@ -158,7 +135,7 @@ private fun String.parseId3v2Genre(): String? {
|
||||||
// ID3v1 tags.
|
// ID3v1 tags.
|
||||||
val genreIds = groups[1]
|
val genreIds = groups[1]
|
||||||
if (genreIds != null && genreIds.value.isNotEmpty()) {
|
if (genreIds != null && genreIds.value.isNotEmpty()) {
|
||||||
val ids = genreIds.value.substring(1 until genreIds.value.lastIndex).split(")(")
|
val ids = genreIds.value.substring(1).split(")(")
|
||||||
for (id in ids) {
|
for (id in ids) {
|
||||||
id.parseId3v1Genre()?.let(genres::add)
|
id.parseId3v1Genre()?.let(genres::add)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,10 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame
|
||||||
import com.google.android.exoplayer2.metadata.vorbis.VorbisComment
|
import com.google.android.exoplayer2.metadata.vorbis.VorbisComment
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.audioUri
|
import org.oxycblt.auxio.music.audioUri
|
||||||
import org.oxycblt.auxio.music.id3GenreName
|
import org.oxycblt.auxio.music.parseId3GenreName
|
||||||
import org.oxycblt.auxio.music.iso8601year
|
import org.oxycblt.auxio.music.parseIso8601Year
|
||||||
import org.oxycblt.auxio.music.plainTrackNo
|
import org.oxycblt.auxio.music.parseNum
|
||||||
import org.oxycblt.auxio.music.trackDiscNo
|
import org.oxycblt.auxio.music.parsePositionNum
|
||||||
import org.oxycblt.auxio.music.year
|
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
import org.oxycblt.auxio.util.logW
|
import org.oxycblt.auxio.util.logW
|
||||||
|
|
||||||
|
@ -211,10 +210,10 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
|
||||||
tags["TSOT"]?.let { audio.sortTitle = it }
|
tags["TSOT"]?.let { audio.sortTitle = it }
|
||||||
|
|
||||||
// Track, as NN/TT
|
// Track, as NN/TT
|
||||||
tags["TRCK"]?.trackDiscNo?.let { audio.track = it }
|
tags["TRCK"]?.parsePositionNum()?.let { audio.track = it }
|
||||||
|
|
||||||
// Disc, as NN/TT
|
// Disc, as NN/TT
|
||||||
tags["TPOS"]?.trackDiscNo?.let { audio.disc = it }
|
tags["TPOS"]?.parsePositionNum()?.let { audio.disc = it }
|
||||||
|
|
||||||
// Dates are somewhat complicated, as not only did their semantics change from a flat year
|
// Dates are somewhat complicated, as not only did their semantics change from a flat year
|
||||||
// value in ID3v2.3 to a full ISO-8601 date in ID3v2.4, but there are also a variety of
|
// value in ID3v2.3 to a full ISO-8601 date in ID3v2.4, but there are also a variety of
|
||||||
|
@ -225,9 +224,9 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
|
||||||
// 3. ID3v2.4 Release Date, as it is the second most common date type
|
// 3. ID3v2.4 Release Date, as it is the second most common date type
|
||||||
// 4. ID3v2.3 Original Date, as it is like #1
|
// 4. ID3v2.3 Original Date, as it is like #1
|
||||||
// 5. ID3v2.3 Release Year, as it is the most common date type
|
// 5. ID3v2.3 Release Year, as it is the most common date type
|
||||||
(tags["TDOR"]?.iso8601year
|
(tags["TDOR"]?.parseIso8601Year()
|
||||||
?: tags["TDRC"]?.iso8601year ?: tags["TDRL"]?.iso8601year ?: tags["TORY"]?.year
|
?: tags["TDRC"]?.parseIso8601Year() ?: tags["TDRL"]?.parseIso8601Year()
|
||||||
?: tags["TYER"]?.year)
|
?: tags["TORY"]?.parseNum() ?: tags["TYER"]?.parseNum())
|
||||||
?.let { audio.year = it }
|
?.let { audio.year = it }
|
||||||
|
|
||||||
// (Sort) Album
|
// (Sort) Album
|
||||||
|
@ -243,7 +242,7 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
|
||||||
tags["TSO2"]?.let { audio.sortAlbumArtist = it }
|
tags["TSO2"]?.let { audio.sortAlbumArtist = it }
|
||||||
|
|
||||||
// Genre, with the weird ID3 rules.
|
// Genre, with the weird ID3 rules.
|
||||||
tags["TCON"]?.let { audio.genre = it.id3GenreName }
|
tags["TCON"]?.let { audio.genre = it.parseId3GenreName() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun populateVorbis(tags: Map<String, String>) {
|
private fun populateVorbis(tags: Map<String, String>) {
|
||||||
|
@ -252,10 +251,10 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
|
||||||
tags["TITLESORT"]?.let { audio.sortTitle = it }
|
tags["TITLESORT"]?.let { audio.sortTitle = it }
|
||||||
|
|
||||||
// Track. Probably not NN/TT, as TOTALTRACKS handles totals.
|
// Track. Probably not NN/TT, as TOTALTRACKS handles totals.
|
||||||
tags["TRACKNUMBER"]?.plainTrackNo?.let { audio.track = it }
|
tags["TRACKNUMBER"]?.parseNum()?.let { audio.track = it }
|
||||||
|
|
||||||
// Disc. Probably not NN/TT, as TOTALDISCS handles totals.
|
// Disc. Probably not NN/TT, as TOTALDISCS handles totals.
|
||||||
tags["DISCNUMBER"]?.plainTrackNo?.let { audio.disc = it }
|
tags["DISCNUMBER"]?.parseNum()?.let { audio.disc = it }
|
||||||
|
|
||||||
// Vorbis dates are less complicated, but there are still several types
|
// Vorbis dates are less complicated, but there are still several types
|
||||||
// Our hierarchy for dates is as such:
|
// Our hierarchy for dates is as such:
|
||||||
|
@ -263,7 +262,8 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
|
||||||
// 2. Date, as it is the most common date type
|
// 2. Date, as it is the most common date type
|
||||||
// 3. Year, as old vorbis tags tended to use this (I know this because it's the only
|
// 3. Year, as old vorbis tags tended to use this (I know this because it's the only
|
||||||
// tag that android supports, so it must be 15 years old or more!)
|
// tag that android supports, so it must be 15 years old or more!)
|
||||||
(tags["ORIGINALDATE"]?.iso8601year ?: tags["DATE"]?.iso8601year ?: tags["YEAR"]?.year)
|
(tags["ORIGINALDATE"]?.parseIso8601Year()
|
||||||
|
?: tags["DATE"]?.parseIso8601Year() ?: tags["YEAR"]?.parseNum())
|
||||||
?.let { audio.year = it }
|
?.let { audio.year = it }
|
||||||
|
|
||||||
// (Sort) Album
|
// (Sort) Album
|
||||||
|
@ -274,7 +274,7 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
|
||||||
tags["ARTIST"]?.let { audio.artist = it }
|
tags["ARTIST"]?.let { audio.artist = it }
|
||||||
tags["ARTISTSORT"]?.let { audio.sortArtist = it }
|
tags["ARTISTSORT"]?.let { audio.sortArtist = it }
|
||||||
|
|
||||||
// (Sort) Album artist.
|
// (Sort) Album artist
|
||||||
tags["ALBUMARTIST"]?.let { audio.albumArtist = it }
|
tags["ALBUMARTIST"]?.let { audio.albumArtist = it }
|
||||||
tags["ALBUMARTISTSORT"]?.let { audio.sortAlbumArtist = it }
|
tags["ALBUMARTISTSORT"]?.let { audio.sortAlbumArtist = it }
|
||||||
|
|
||||||
|
|
|
@ -34,13 +34,13 @@ import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.albumCoverUri
|
import org.oxycblt.auxio.music.albumCoverUri
|
||||||
import org.oxycblt.auxio.music.audioUri
|
import org.oxycblt.auxio.music.audioUri
|
||||||
import org.oxycblt.auxio.music.directoryCompat
|
import org.oxycblt.auxio.music.directoryCompat
|
||||||
import org.oxycblt.auxio.music.id3GenreName
|
|
||||||
import org.oxycblt.auxio.music.mediaStoreVolumeNameCompat
|
import org.oxycblt.auxio.music.mediaStoreVolumeNameCompat
|
||||||
import org.oxycblt.auxio.music.packedDiscNo
|
import org.oxycblt.auxio.music.parseId3GenreName
|
||||||
import org.oxycblt.auxio.music.packedTrackNo
|
import org.oxycblt.auxio.music.parsePositionNum
|
||||||
import org.oxycblt.auxio.music.queryCursor
|
import org.oxycblt.auxio.music.queryCursor
|
||||||
import org.oxycblt.auxio.music.storageVolumesCompat
|
import org.oxycblt.auxio.music.storageVolumesCompat
|
||||||
import org.oxycblt.auxio.music.trackDiscNo
|
import org.oxycblt.auxio.music.unpackDiscNo
|
||||||
|
import org.oxycblt.auxio.music.unpackTrackNo
|
||||||
import org.oxycblt.auxio.music.useQuery
|
import org.oxycblt.auxio.music.useQuery
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import org.oxycblt.auxio.util.contentResolverSafe
|
import org.oxycblt.auxio.util.contentResolverSafe
|
||||||
|
@ -203,7 +203,7 @@ abstract class MediaStoreBackend : Indexer.Backend {
|
||||||
// format a genre was derived from, we have to treat them like they are ID3
|
// format a genre was derived from, we have to treat them like they are ID3
|
||||||
// genres, even when they might not be.
|
// genres, even when they might not be.
|
||||||
val id = genreCursor.getLong(idIndex)
|
val id = genreCursor.getLong(idIndex)
|
||||||
val name = (genreCursor.getStringOrNull(nameIndex) ?: continue).id3GenreName
|
val name = (genreCursor.getStringOrNull(nameIndex) ?: continue).parseId3GenreName()
|
||||||
|
|
||||||
context.contentResolverSafe.useQuery(
|
context.contentResolverSafe.useQuery(
|
||||||
MediaStore.Audio.Genres.Members.getContentUri(VOLUME_EXTERNAL, id),
|
MediaStore.Audio.Genres.Members.getContentUri(VOLUME_EXTERNAL, id),
|
||||||
|
@ -350,40 +350,36 @@ abstract class MediaStoreBackend : Indexer.Backend {
|
||||||
) {
|
) {
|
||||||
fun toSong() =
|
fun toSong() =
|
||||||
Song(
|
Song(
|
||||||
// Assert that the fields that should always exist are present. I can't confirm
|
// Assert that the fields that should always exist are present. I can't confirm
|
||||||
// that
|
// that
|
||||||
// every device provides these fields, but it seems likely that they do.
|
// every device provides these fields, but it seems likely that they do.
|
||||||
rawName = requireNotNull(title) { "Malformed audio: No title" },
|
rawName = requireNotNull(title) { "Malformed audio: No title" },
|
||||||
rawSortName = sortTitle,
|
rawSortName = sortTitle,
|
||||||
path =
|
path =
|
||||||
Path(
|
Path(
|
||||||
name =
|
name = requireNotNull(displayName) { "Malformed audio: No display name" },
|
||||||
requireNotNull(displayName) { "Malformed audio: No display name" },
|
parent = requireNotNull(dir) { "Malformed audio: No parent directory" }),
|
||||||
parent =
|
uri = requireNotNull(id) { "Malformed audio: No id" }.audioUri,
|
||||||
requireNotNull(dir) { "Malformed audio: No parent directory" }),
|
mimeType =
|
||||||
uri = requireNotNull(id) { "Malformed audio: No id" }.audioUri,
|
MimeType(
|
||||||
mimeType =
|
fromExtension =
|
||||||
MimeType(
|
requireNotNull(extensionMimeType) { "Malformed audio: No mime type" },
|
||||||
fromExtension =
|
fromFormat = formatMimeType),
|
||||||
requireNotNull(extensionMimeType) {
|
size = requireNotNull(size) { "Malformed audio: No size" },
|
||||||
"Malformed audio: No mime type"
|
dateAdded = requireNotNull(dateAdded) { "Malformed audio: No date added" },
|
||||||
},
|
durationMs = requireNotNull(duration) { "Malformed audio: No duration" },
|
||||||
fromFormat = formatMimeType),
|
track = track,
|
||||||
size = requireNotNull(size) { "Malformed audio: No size" },
|
disc = disc,
|
||||||
dateAdded = requireNotNull(dateAdded) { "Malformed audio: No date added" },
|
_year = year,
|
||||||
durationMs = requireNotNull(duration) { "Malformed audio: No duration" },
|
_albumName = requireNotNull(album) { "Malformed audio: No album name" },
|
||||||
track = track,
|
_albumSortName = sortAlbum,
|
||||||
disc = disc,
|
_albumCoverUri =
|
||||||
_year = year,
|
requireNotNull(albumId) { "Malformed audio: No album id" }.albumCoverUri,
|
||||||
_albumName = requireNotNull(album) { "Malformed audio: No album name" },
|
_artistName = artist,
|
||||||
_albumSortName = sortAlbum,
|
_artistSortName = sortArtist,
|
||||||
_albumCoverUri =
|
_albumArtistName = albumArtist,
|
||||||
requireNotNull(albumId) { "Malformed audio: No album id" }.albumCoverUri,
|
_albumArtistSortName = sortAlbumArtist,
|
||||||
_artistName = artist,
|
_genreName = genre)
|
||||||
_artistSortName = sortArtist,
|
|
||||||
_albumArtistName = albumArtist,
|
|
||||||
_albumArtistSortName = sortAlbumArtist,
|
|
||||||
_genreName = genre)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -469,8 +465,8 @@ class Api21MediaStoreBackend : MediaStoreBackend() {
|
||||||
|
|
||||||
val rawTrack = cursor.getIntOrNull(trackIndex)
|
val rawTrack = cursor.getIntOrNull(trackIndex)
|
||||||
if (rawTrack != null) {
|
if (rawTrack != null) {
|
||||||
rawTrack.packedTrackNo?.let { audio.track = it }
|
rawTrack.unpackTrackNo()?.let { audio.track = it }
|
||||||
rawTrack.packedDiscNo?.let { audio.disc = it }
|
rawTrack.unpackDiscNo()?.let { audio.disc = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
return audio
|
return audio
|
||||||
|
@ -555,8 +551,8 @@ open class Api29MediaStoreBackend : BaseApi29MediaStoreBackend() {
|
||||||
// Use the old field instead.
|
// Use the old field instead.
|
||||||
val rawTrack = cursor.getIntOrNull(trackIndex)
|
val rawTrack = cursor.getIntOrNull(trackIndex)
|
||||||
if (rawTrack != null) {
|
if (rawTrack != null) {
|
||||||
rawTrack.packedTrackNo?.let { audio.track = it }
|
rawTrack.unpackTrackNo()?.let { audio.track = it }
|
||||||
rawTrack.packedDiscNo?.let { audio.disc = it }
|
rawTrack.unpackDiscNo()?.let { audio.disc = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
return audio
|
return audio
|
||||||
|
@ -594,8 +590,8 @@ class Api30MediaStoreBackend : BaseApi29MediaStoreBackend() {
|
||||||
// N is the number and T is the total. Parse the number while leaving out the
|
// N is the number and T is the total. Parse the number while leaving out the
|
||||||
// total, as we have no use for it.
|
// total, as we have no use for it.
|
||||||
|
|
||||||
cursor.getStringOrNull(trackIndex)?.trackDiscNo?.let { audio.track = it }
|
cursor.getStringOrNull(trackIndex)?.parsePositionNum()?.let { audio.track = it }
|
||||||
cursor.getStringOrNull(discIndex)?.trackDiscNo?.let { audio.disc = it }
|
cursor.getStringOrNull(discIndex)?.parsePositionNum()?.let { audio.disc = it }
|
||||||
|
|
||||||
return audio
|
return audio
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue