musikr: replace mimetype w/format
First property now derived from taglib.
This commit is contained in:
parent
e16b23f34e
commit
9ab4dc5595
13 changed files with 162 additions and 148 deletions
|
@ -104,9 +104,9 @@ class SongDetailDialog : ViewBindingMaterialDialogFragment<DialogSongDetailBindi
|
|||
add(SongProperty(R.string.lbl_disc, zipped))
|
||||
}
|
||||
add(SongProperty(R.string.lbl_path, song.path.resolve(context)))
|
||||
info.resolvedMimeType.resolveName(context)?.let {
|
||||
add(SongProperty(R.string.lbl_format, it))
|
||||
}
|
||||
// info.format.resolveName(context)?.let {
|
||||
// add(SongProperty(R.string.lbl_format, it))
|
||||
// }
|
||||
add(
|
||||
SongProperty(
|
||||
R.string.lbl_size, Formatter.formatFileSize(context, song.size)))
|
||||
|
|
|
@ -320,7 +320,7 @@ fun Context.share(songs: Collection<Song>) {
|
|||
val mimeTypes = mutableSetOf<String>()
|
||||
for (song in songs) {
|
||||
builder.addStream(song.uri)
|
||||
mimeTypes.add(song.mimeType.fromFormat ?: song.mimeType.fromExtension)
|
||||
mimeTypes.add(song.format.mimeType)
|
||||
}
|
||||
|
||||
builder.setType(mimeTypes.singleOrNull() ?: "audio/*").startChooser()
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment
|
|||
import org.oxycblt.auxio.util.concatLocalized
|
||||
import org.oxycblt.auxio.util.toUuidOrNull
|
||||
import org.oxycblt.musikr.cover.Cover
|
||||
import org.oxycblt.musikr.fs.MimeType
|
||||
import org.oxycblt.musikr.fs.Format
|
||||
import org.oxycblt.musikr.fs.Path
|
||||
import org.oxycblt.musikr.tag.Date
|
||||
import org.oxycblt.musikr.tag.Disc
|
||||
|
@ -252,8 +252,8 @@ interface Song : Music {
|
|||
* instead for accessing the audio file.
|
||||
*/
|
||||
val path: Path
|
||||
/** The [MimeType] of the audio file. Only intended for display. */
|
||||
val mimeType: MimeType
|
||||
/** The [Format] of the audio file. Only intended for display. */
|
||||
val format: Format
|
||||
/** The size of the audio file, in bytes. */
|
||||
val size: Long
|
||||
/** The duration of the audio file, in milliseconds. */
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.oxycblt.musikr.cache
|
||||
|
||||
import org.oxycblt.ktaglib.Properties
|
||||
import org.oxycblt.musikr.cover.Cover
|
||||
import org.oxycblt.musikr.fs.query.DeviceFile
|
||||
import org.oxycblt.musikr.tag.parse.ParsedTags
|
||||
|
@ -34,7 +35,11 @@ interface Cache {
|
|||
}
|
||||
}
|
||||
|
||||
data class CachedSong(val parsedTags: ParsedTags, val cover: Cover.Single?)
|
||||
data class CachedSong(
|
||||
val parsedTags: ParsedTags,
|
||||
val cover: Cover.Single?,
|
||||
val properties: Properties
|
||||
)
|
||||
|
||||
private class FullCache(private val cacheInfoDao: CacheInfoDao) : Cache {
|
||||
override suspend fun read(file: DeviceFile) =
|
||||
|
|
|
@ -30,6 +30,7 @@ import androidx.room.Room
|
|||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverter
|
||||
import androidx.room.TypeConverters
|
||||
import org.oxycblt.ktaglib.Properties
|
||||
import org.oxycblt.musikr.cover.Cover
|
||||
import org.oxycblt.musikr.fs.query.DeviceFile
|
||||
import org.oxycblt.musikr.tag.Date
|
||||
|
@ -67,7 +68,6 @@ internal data class CachedInfo(
|
|||
*/
|
||||
@PrimaryKey val uri: String,
|
||||
val dateModified: Long,
|
||||
val durationMs: Long,
|
||||
val replayGainTrackAdjustment: Float?,
|
||||
val replayGainAlbumAdjustment: Float?,
|
||||
val musicBrainzId: String?,
|
||||
|
@ -88,7 +88,11 @@ internal data class CachedInfo(
|
|||
val albumArtistNames: List<String>,
|
||||
val albumArtistSortNames: List<String>,
|
||||
val genreNames: List<String>,
|
||||
val cover: Cover.Single? = null
|
||||
val cover: Cover.Single?,
|
||||
val mimeType: String,
|
||||
val durationMs: Long,
|
||||
val bitrate: Int,
|
||||
val sampleRate: Int,
|
||||
) {
|
||||
fun intoCachedSong() =
|
||||
CachedSong(
|
||||
|
@ -114,7 +118,8 @@ internal data class CachedInfo(
|
|||
albumArtistNames = albumArtistNames,
|
||||
albumArtistSortNames = albumArtistSortNames,
|
||||
genreNames = genreNames),
|
||||
cover)
|
||||
cover,
|
||||
Properties(mimeType, durationMs, bitrate, sampleRate))
|
||||
|
||||
object Converters {
|
||||
@TypeConverter
|
||||
|
@ -159,6 +164,9 @@ internal data class CachedInfo(
|
|||
albumArtistNames = cachedSong.parsedTags.albumArtistNames,
|
||||
albumArtistSortNames = cachedSong.parsedTags.albumArtistSortNames,
|
||||
genreNames = cachedSong.parsedTags.genreNames,
|
||||
cover = cachedSong.cover)
|
||||
cover = cachedSong.cover,
|
||||
mimeType = cachedSong.properties.mimeType,
|
||||
bitrate = cachedSong.properties.bitrate,
|
||||
sampleRate = cachedSong.properties.sampleRate)
|
||||
}
|
||||
}
|
||||
|
|
112
app/src/main/java/org/oxycblt/musikr/fs/Format.kt
Normal file
112
app/src/main/java/org/oxycblt/musikr/fs/Format.kt
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* Format.kt is part of Auxio.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.musikr.fs
|
||||
|
||||
import android.webkit.MimeTypeMap
|
||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
||||
sealed interface Format {
|
||||
val mimeType: String
|
||||
|
||||
data object MPEG3 : Format {
|
||||
override val mimeType = "audio/mpeg"
|
||||
}
|
||||
|
||||
data class MPEG4(val containing: Format?) : Format {
|
||||
override val mimeType = "audio/mp4"
|
||||
}
|
||||
|
||||
data object AAC : Format {
|
||||
override val mimeType = "audio/aac"
|
||||
}
|
||||
|
||||
data object ALAC : Format {
|
||||
override val mimeType = "audio/alac"
|
||||
}
|
||||
|
||||
data class Ogg(val containing: Format?) : Format {
|
||||
override val mimeType = "audio/ogg"
|
||||
}
|
||||
|
||||
data object Opus : Format {
|
||||
override val mimeType = "audio/opus"
|
||||
}
|
||||
|
||||
data object Vorbis : Format {
|
||||
override val mimeType = "audio/vorbis"
|
||||
}
|
||||
|
||||
data object FLAC : Format {
|
||||
override val mimeType = "audio/flac"
|
||||
}
|
||||
|
||||
data object Wav : Format {
|
||||
override val mimeType = "audio/wav"
|
||||
}
|
||||
|
||||
data class Unknown(override val mimeType: String) : Format {
|
||||
val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)?.uppercase()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val CODEC_MAP =
|
||||
mapOf(
|
||||
"audio/mpeg" to MPEG3,
|
||||
"audio/mp3" to MPEG3,
|
||||
"audio/aac" to AAC,
|
||||
"audio/aacp" to AAC,
|
||||
"audio/3gpp" to AAC,
|
||||
"audio/3gpp2" to AAC,
|
||||
"audio/alac" to ALAC,
|
||||
"audio/opus" to Opus,
|
||||
"audio/vorbis" to Vorbis,
|
||||
"audio/flac" to FLAC,
|
||||
"audio/wav" to Wav,
|
||||
"audio/raw" to Wav,
|
||||
"audio/x-wav" to Wav,
|
||||
"audio/vnd.wave" to Wav,
|
||||
"audio/wave" to Wav,
|
||||
)
|
||||
|
||||
fun infer(containerMimeType: String, codecMimeType: String): Format {
|
||||
val codecFormat = CODEC_MAP[codecMimeType]
|
||||
if (codecFormat != null) {
|
||||
// Codec found, possibly wrap in container.
|
||||
return unlikelyToBeNull(wrapInContainer(containerMimeType, codecFormat))
|
||||
}
|
||||
val extensionFormat = CODEC_MAP[containerMimeType]
|
||||
if (extensionFormat != null) {
|
||||
// Standalone container of some codec.
|
||||
return extensionFormat
|
||||
}
|
||||
return wrapInContainer(containerMimeType, null) ?: Unknown(containerMimeType)
|
||||
}
|
||||
|
||||
private fun wrapInContainer(containerMimeType: String, format: Format?) =
|
||||
when (containerMimeType) {
|
||||
"audio/mp4",
|
||||
"audio/mp4a-latm",
|
||||
"audio/mpeg4-generic" -> MPEG4(format)
|
||||
"audio/ogg",
|
||||
"application/ogg",
|
||||
"application/x-ogg" -> Ogg(format)
|
||||
else -> format
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* MimeType.kt is part of Auxio.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.musikr.fs
|
||||
|
||||
import android.content.Context
|
||||
import android.media.MediaFormat
|
||||
import android.webkit.MimeTypeMap
|
||||
import org.oxycblt.auxio.R
|
||||
|
||||
/**
|
||||
* A mime type of a file. Only intended for display.
|
||||
*
|
||||
* @param fromExtension The mime type obtained by analyzing the file extension.
|
||||
* @param fromFormat The mime type obtained by analyzing the file format. Null if could not be
|
||||
* obtained.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*
|
||||
* TODO: Get around to simplifying this
|
||||
*/
|
||||
data class MimeType(val fromExtension: String, val fromFormat: String?) {
|
||||
/**
|
||||
* Resolve the mime type into a human-readable format name, such as "Ogg Vorbis".
|
||||
*
|
||||
* @param context [Context] required to obtain human-readable strings.
|
||||
* @return A human-readable name for this mime type. Will first try [fromFormat], then falling
|
||||
* back to [fromExtension], and then null if that fails.
|
||||
*/
|
||||
fun resolveName(context: Context): String? {
|
||||
// We try our best to produce a more readable name for the common audio formats.
|
||||
val formatName =
|
||||
when (fromFormat) {
|
||||
// We start with the extracted mime types, as they are more consistent. Note that
|
||||
// we do not include container formats at all with these names. It is only the
|
||||
// inner codec that we bother with.
|
||||
MediaFormat.MIMETYPE_AUDIO_MPEG -> R.string.cdc_mp3
|
||||
MediaFormat.MIMETYPE_AUDIO_AAC -> R.string.cdc_aac
|
||||
MediaFormat.MIMETYPE_AUDIO_VORBIS -> R.string.cdc_vorbis
|
||||
MediaFormat.MIMETYPE_AUDIO_OPUS -> R.string.cdc_opus
|
||||
MediaFormat.MIMETYPE_AUDIO_FLAC -> R.string.cdc_flac
|
||||
// TODO: Add ALAC to this as soon as I can stop using MediaFormat for
|
||||
// extracting metadata and just use ExoPlayer.
|
||||
// We don't give a name to more unpopular formats.
|
||||
else -> -1
|
||||
}
|
||||
|
||||
if (formatName > -1) {
|
||||
return context.getString(formatName)
|
||||
}
|
||||
|
||||
// Fall back to the file extension in the case that we have no mime type or
|
||||
// a useless "audio/raw" mime type. Here:
|
||||
// - We return names for container formats instead of the inner format, as we
|
||||
// cannot parse the file.
|
||||
// - We are at the mercy of the Android OS, hence we check for every possible mime
|
||||
// type for a particular format according to Wikipedia.
|
||||
val extensionName =
|
||||
when (fromExtension) {
|
||||
"audio/mpeg",
|
||||
"audio/mp3" -> R.string.cdc_mp3
|
||||
"audio/mp4",
|
||||
"audio/mp4a-latm",
|
||||
"audio/mpeg4-generic" -> R.string.cdc_mp4
|
||||
"audio/aac",
|
||||
"audio/aacp",
|
||||
"audio/3gpp",
|
||||
"audio/3gpp2" -> R.string.cdc_aac
|
||||
"audio/ogg",
|
||||
"application/ogg",
|
||||
"application/x-ogg" -> R.string.cdc_ogg
|
||||
"audio/flac" -> R.string.cdc_flac
|
||||
"audio/wav",
|
||||
"audio/x-wav",
|
||||
"audio/wave",
|
||||
"audio/vnd.wave" -> R.string.cdc_wav
|
||||
"audio/x-matroska" -> R.string.cdc_mka
|
||||
else -> -1
|
||||
}
|
||||
|
||||
return if (extensionName > -1) {
|
||||
context.getString(extensionName)
|
||||
} else {
|
||||
// Fall back to the extension if we can't find a special name for this format.
|
||||
MimeTypeMap.getSingleton().getExtensionFromMimeType(fromExtension)?.uppercase()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ import android.media.MediaFormat
|
|||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import org.oxycblt.musikr.Song
|
||||
import org.oxycblt.musikr.fs.MimeType
|
||||
import timber.log.Timber as L
|
||||
|
||||
/**
|
||||
|
@ -32,14 +31,9 @@ import timber.log.Timber as L
|
|||
*
|
||||
* @param bitrateKbps The bit rate, in kilobytes-per-second. Null if it could not be parsed.
|
||||
* @param sampleRateHz The sample rate, in hertz.
|
||||
* @param resolvedMimeType The known mime type of the [Song] after it's file format was determined.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
data class AudioProperties(
|
||||
val bitrateKbps: Int?,
|
||||
val sampleRateHz: Int?,
|
||||
val resolvedMimeType: MimeType
|
||||
) {
|
||||
data class AudioProperties(val bitrateKbps: Int?, val sampleRateHz: Int?) {
|
||||
/** Implements the process of extracting [AudioProperties] from a given [Song]. */
|
||||
interface Factory {
|
||||
/**
|
||||
|
@ -75,7 +69,7 @@ constructor(@ApplicationContext private val context: Context) : AudioProperties.
|
|||
// that we can show.
|
||||
L.w("Unable to extract song attributes.")
|
||||
L.w(e.stackTraceToString())
|
||||
return AudioProperties(null, null, song.mimeType)
|
||||
return AudioProperties(null, null)
|
||||
}
|
||||
|
||||
// Get the first track from the extractor (This is basically always the only
|
||||
|
@ -102,23 +96,10 @@ constructor(@ApplicationContext private val context: Context) : AudioProperties.
|
|||
null
|
||||
}
|
||||
|
||||
// The song's mime type won't have a populated format field right now, try to
|
||||
// extract it ourselves.
|
||||
val formatMimeType =
|
||||
try {
|
||||
format.getString(MediaFormat.KEY_MIME)
|
||||
} catch (e: NullPointerException) {
|
||||
L.e("Unable to extract mime type field")
|
||||
null
|
||||
}
|
||||
|
||||
extractor.release()
|
||||
|
||||
L.d("Finished extracting audio properties")
|
||||
|
||||
return AudioProperties(
|
||||
bitrate,
|
||||
sampleRate,
|
||||
MimeType(fromExtension = song.mimeType.fromExtension, fromFormat = formatMimeType))
|
||||
return AudioProperties(bitrate, sampleRate)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ class SongImpl(private val handle: SongCore) : Song {
|
|||
override val date = preSong.date
|
||||
override val uri = preSong.uri
|
||||
override val path = preSong.path
|
||||
override val mimeType = preSong.mimeType
|
||||
override val format = preSong.format
|
||||
override val size = preSong.size
|
||||
override val durationMs = preSong.durationMs
|
||||
override val replayGainAdjustment = preSong.replayGainAdjustment
|
||||
|
|
|
@ -53,7 +53,10 @@ private class EvaluateStepImpl(
|
|||
val preSongs =
|
||||
extractedMusic
|
||||
.filterIsInstance<ExtractedMusic.Song>()
|
||||
.map { tagInterpreter.interpret(it.file, it.tags, it.cover, interpretation) }
|
||||
.map {
|
||||
tagInterpreter.interpret(
|
||||
it.file, it.tags, it.cover, it.properties, interpretation)
|
||||
}
|
||||
.flowOn(Dispatchers.Main)
|
||||
.buffer(Channel.UNLIMITED)
|
||||
val graphBuilder = MusicGraph.builder()
|
||||
|
|
|
@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.flowOn
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import org.oxycblt.ktaglib.Properties
|
||||
import org.oxycblt.musikr.Storage
|
||||
import org.oxycblt.musikr.cache.CachedSong
|
||||
import org.oxycblt.musikr.cover.Cover
|
||||
|
@ -63,7 +64,7 @@ private class ExtractStepImpl(
|
|||
val (cachedSongs, uncachedSongs) =
|
||||
cacheResults.mapPartition {
|
||||
it.cachedSong?.let { song ->
|
||||
ExtractedMusic.Song(it.file, song.parsedTags, song.cover)
|
||||
ExtractedMusic.Song(it.file, song.properties, song.parsedTags, song.cover)
|
||||
}
|
||||
}
|
||||
val split = uncachedSongs.distribute(16)
|
||||
|
@ -73,10 +74,9 @@ private class ExtractStepImpl(
|
|||
.mapNotNull { node ->
|
||||
val metadata =
|
||||
metadataExtractor.extract(node.file) ?: return@mapNotNull null
|
||||
L.d("Extracted tags for ${metadata.id3v2}")
|
||||
val tags = tagParser.parse(node.file, metadata)
|
||||
val cover = metadata.cover?.let { storage.storedCovers.write(it) }
|
||||
ExtractedMusic.Song(node.file, tags, cover)
|
||||
ExtractedMusic.Song(node.file, metadata.properties, tags, cover)
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.buffer(Channel.UNLIMITED)
|
||||
|
@ -84,7 +84,7 @@ private class ExtractStepImpl(
|
|||
val writtenSongs =
|
||||
merge(*extractedSongs)
|
||||
.map {
|
||||
storage.cache.write(it.file, CachedSong(it.tags, it.cover))
|
||||
storage.cache.write(it.file, CachedSong(it.tags, it.cover, it.properties))
|
||||
it
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
|
@ -100,6 +100,10 @@ private class ExtractStepImpl(
|
|||
}
|
||||
|
||||
sealed interface ExtractedMusic {
|
||||
data class Song(val file: DeviceFile, val tags: ParsedTags, val cover: Cover.Single?) :
|
||||
ExtractedMusic
|
||||
data class Song(
|
||||
val file: DeviceFile,
|
||||
val properties: Properties,
|
||||
val tags: ParsedTags,
|
||||
val cover: Cover.Single?
|
||||
) : ExtractedMusic
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment
|
|||
import org.oxycblt.auxio.util.update
|
||||
import org.oxycblt.musikr.Music
|
||||
import org.oxycblt.musikr.cover.Cover
|
||||
import org.oxycblt.musikr.fs.MimeType
|
||||
import org.oxycblt.musikr.fs.Format
|
||||
import org.oxycblt.musikr.fs.Path
|
||||
import org.oxycblt.musikr.playlist.PlaylistHandle
|
||||
import org.oxycblt.musikr.tag.Date
|
||||
|
@ -42,7 +42,7 @@ data class PreSong(
|
|||
val date: Date?,
|
||||
val uri: Uri,
|
||||
val path: Path,
|
||||
val mimeType: MimeType,
|
||||
val format: Format,
|
||||
val size: Long,
|
||||
val durationMs: Long,
|
||||
val replayGainAdjustment: ReplayGainAdjustment,
|
||||
|
|
|
@ -21,9 +21,10 @@ package org.oxycblt.musikr.tag.interpret
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment
|
||||
import org.oxycblt.auxio.util.toUuidOrNull
|
||||
import org.oxycblt.ktaglib.Properties
|
||||
import org.oxycblt.musikr.Interpretation
|
||||
import org.oxycblt.musikr.cover.Cover
|
||||
import org.oxycblt.musikr.fs.MimeType
|
||||
import org.oxycblt.musikr.fs.Format
|
||||
import org.oxycblt.musikr.fs.query.DeviceFile
|
||||
import org.oxycblt.musikr.tag.Disc
|
||||
import org.oxycblt.musikr.tag.Name
|
||||
|
@ -36,6 +37,7 @@ interface TagInterpreter {
|
|||
file: DeviceFile,
|
||||
parsedTags: ParsedTags,
|
||||
cover: Cover.Single?,
|
||||
properties: Properties,
|
||||
interpretation: Interpretation
|
||||
): PreSong
|
||||
|
||||
|
@ -49,6 +51,7 @@ private data object TagInterpreterImpl : TagInterpreter {
|
|||
file: DeviceFile,
|
||||
parsedTags: ParsedTags,
|
||||
cover: Cover.Single?,
|
||||
properties: Properties,
|
||||
interpretation: Interpretation
|
||||
): PreSong {
|
||||
val individualPreArtists =
|
||||
|
@ -79,7 +82,6 @@ private data object TagInterpreterImpl : TagInterpreter {
|
|||
date = parsedTags.date,
|
||||
uri = uri,
|
||||
path = file.path,
|
||||
mimeType = MimeType(file.mimeType, null),
|
||||
size = file.size,
|
||||
durationMs = parsedTags.durationMs,
|
||||
replayGainAdjustment =
|
||||
|
@ -87,6 +89,7 @@ private data object TagInterpreterImpl : TagInterpreter {
|
|||
parsedTags.replayGainTrackAdjustment,
|
||||
parsedTags.replayGainAlbumAdjustment,
|
||||
),
|
||||
format = Format.infer(file.mimeType, properties.mimeType),
|
||||
lastModified = file.lastModified,
|
||||
// TODO: Figure out what to do with date added
|
||||
dateAdded = file.lastModified,
|
||||
|
|
Loading…
Reference in a new issue