diff --git a/app/src/main/java/io/github/zyrouge/symphony/services/groove/MediaExposer.kt b/app/src/main/java/io/github/zyrouge/symphony/services/groove/MediaExposer.kt index a527ab4a..4a7ead5c 100644 --- a/app/src/main/java/io/github/zyrouge/symphony/services/groove/MediaExposer.kt +++ b/app/src/main/java/io/github/zyrouge/symphony/services/groove/MediaExposer.kt @@ -38,6 +38,7 @@ class MediaExposer(private val symphony: Symphony) { val artworkCacheUnused: ConcurrentSet, val lyricsCacheUnused: ConcurrentSet, val filter: MediaFilter, + val songParseOptions: Song.ParseOptions, ) { companion object { suspend fun create(symphony: Symphony): ScanCycle { @@ -56,6 +57,7 @@ class MediaExposer(private val symphony: Symphony) { artworkCacheUnused = artworkCacheUnused, lyricsCacheUnused = lyricsCacheUnused, filter = filter, + songParseOptions = Song.ParseOptions.create(symphony), ) } } @@ -128,7 +130,7 @@ class MediaExposer(private val symphony: Symphony) { && (cached.coverFile?.let { cycle.artworkCacheUnused.contains(it) } != false) val song = when { cacheHit -> cached - else -> Song.parse(symphony, path, file) + else -> Song.parse(path, file, cycle.songParseOptions) } if (song.duration.milliseconds < symphony.settings.minSongDuration.value.seconds) { return diff --git a/app/src/main/java/io/github/zyrouge/symphony/services/groove/Song.kt b/app/src/main/java/io/github/zyrouge/symphony/services/groove/Song.kt index 5925bf3c..534f3003 100644 --- a/app/src/main/java/io/github/zyrouge/symphony/services/groove/Song.kt +++ b/app/src/main/java/io/github/zyrouge/symphony/services/groove/Song.kt @@ -17,6 +17,7 @@ import me.zyrouge.symphony.metaphony.AudioMetadataParser import java.io.FileOutputStream import java.math.RoundingMode import java.time.LocalDate +import java.util.regex.Pattern @Immutable @Entity("songs") @@ -46,6 +47,20 @@ data class Song( val uri: Uri, val path: String, ) { + data class ParseOptions( + val symphony: Symphony, + val artistSeparatorRegex: Regex, + val genreSeparatorRegex: Regex, + ) { + companion object { + fun create(symphony: Symphony) = ParseOptions( + symphony = symphony, + artistSeparatorRegex = makeSeparatorsRegex(symphony.settings.artistTagSeparators.value), + genreSeparatorRegex = makeSeparatorsRegex(symphony.settings.genreTagSeparators.value), + ) + } + } + val bitrateK: Long? get() = bitrate?.let { it / 1000 } val samplingRateK: Float? get() = samplingRate?.let { @@ -83,10 +98,14 @@ data class Song( } companion object { - fun parse(symphony: Symphony, path: SimplePath, file: DocumentFileX): Song { - if (symphony.settings.useMetaphony.value) { + fun parse( + path: SimplePath, + file: DocumentFileX, + options: ParseOptions, + ): Song { + if (options.symphony.settings.useMetaphony.value) { try { - val song = parseUsingMetaphony(symphony, path, file) + val song = parseUsingMetaphony(path, file, options) if (song != null) { return song } @@ -94,14 +113,15 @@ data class Song( Logger.error("Song", "could not parse using metaphony", err) } } - return parseUsingMediaMetadataRetriever(symphony, path, file) + return parseUsingMediaMetadataRetriever(path, file, options) } private fun parseUsingMetaphony( - symphony: Symphony, path: SimplePath, file: DocumentFileX, + options: ParseOptions, ): Song? { + val symphony = options.symphony val metadata = symphony.applicationContext.contentResolver .openFileDescriptor(file.uri, "r") ?.use { AudioMetadataParser.parse(file.name, it.detachFd()) } @@ -134,16 +154,14 @@ data class Song( metadata.lyrics?.let { symphony.database.lyricsCache.put(id, it) } - val artistSeparators = symphony.settings.artistTagSeparators.value - val genreSeparators = symphony.settings.genreTagSeparators.value return Song( id = id, title = metadata.title ?: path.nameWithoutExtension, album = metadata.album, - artists = parseMultiValue(metadata.artists, artistSeparators), - composers = parseMultiValue(metadata.composers, artistSeparators), - albumArtists = parseMultiValue(metadata.albumArtists, artistSeparators), - genres = parseMultiValue(metadata.genres, genreSeparators), + artists = parseMultiValue(metadata.artists, options.artistSeparatorRegex), + composers = parseMultiValue(metadata.composers, options.artistSeparatorRegex), + albumArtists = parseMultiValue(metadata.albumArtists, options.artistSeparatorRegex), + genres = parseMultiValue(metadata.genres, options.genreSeparatorRegex), trackNumber = metadata.trackNumber, trackTotal = metadata.trackTotal, discNumber = metadata.discNumber, @@ -164,10 +182,11 @@ data class Song( } fun parseUsingMediaMetadataRetriever( - symphony: Symphony, path: SimplePath, file: DocumentFileX, + options: ParseOptions, ): Song { + val symphony = options.symphony val retriever = MediaMetadataRetriever() retriever.setDataSource(symphony.applicationContext, file.uri) val id = symphony.groove.song.idGenerator.next() + ".mr" @@ -211,16 +230,14 @@ data class Song( .extractMetadata(MediaMetadataRetriever.METADATA_KEY_SAMPLERATE) ?.toLongOrNull() } - val artistSeparators = symphony.settings.artistTagSeparators.value - val genreSeparators = symphony.settings.genreTagSeparators.value return Song( id = id, title = title ?: path.nameWithoutExtension, album = album, - artists = parseMultiValue(artists, artistSeparators), - composers = parseMultiValue(composers, artistSeparators), - albumArtists = parseMultiValue(albumArtists, artistSeparators), - genres = parseMultiValue(genres, genreSeparators), + artists = parseMultiValue(artists, options.artistSeparatorRegex), + composers = parseMultiValue(composers, options.artistSeparatorRegex), + albumArtists = parseMultiValue(albumArtists, options.artistSeparatorRegex), + genres = parseMultiValue(genres, options.genreSeparatorRegex), trackNumber = trackNumber, trackTotal = trackTotal, discNumber = discNumber, @@ -240,14 +257,19 @@ data class Song( ) } - fun parseMultiValue(value: String?, separators: Set) = value?.let { - parseMultiValue(setOf(it), separators) + private fun makeSeparatorsRegex(separators: Set): Regex { + val partial = separators.joinToString("|") { Pattern.quote(it) } + return Regex("""(?, separators: Set): Set { + fun parseMultiValue(values: Set, regex: Regex): Set { val result = mutableSetOf() for (x in values) { - for (y in x.trim().split(*separators.toTypedArray())) { + for (y in x.trim().split(regex)) { val trimmed = y.trim() if (trimmed.isEmpty()) { continue