musikr: include context in pipeline errors

This commit is contained in:
Alexander Capehart 2024-12-17 16:01:44 -05:00
parent a1188b8d4b
commit acd4dab74c
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 91 additions and 14 deletions

View file

@ -66,20 +66,20 @@ private class EvaluateStepImpl(
val rawSongs = filterFlow.right
val preSongs =
rawSongs
.map { tagInterpreter.interpret(it) }
.map { wrap(it, tagInterpreter::interpret) }
.flowOn(Dispatchers.Default)
.buffer(Channel.UNLIMITED)
val prePlaylists =
filterFlow.left
.map { playlistInterpreter.interpret(it) }
.map { wrap(it, playlistInterpreter::interpret) }
.flowOn(Dispatchers.Default)
.buffer(Channel.UNLIMITED)
val graphBuilder = MusicGraph.builder()
val graphBuild =
merge(
filterFlow.manager,
preSongs.onEach { graphBuilder.add(it) },
prePlaylists.onEach { graphBuilder.add(it) })
preSongs.onEach { wrap(it, graphBuilder::add) },
prePlaylists.onEach { wrap(it, graphBuilder::add) })
graphBuild.collect()
val graph = graphBuilder.build()
return libraryFactory.create(graph, storedPlaylists, playlistInterpreter)

View file

@ -15,7 +15,7 @@
* 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.pipeline
import android.content.Context
@ -48,7 +48,8 @@ internal interface ExtractStep {
MetadataExtractor.from(context),
TagParser.new(),
storage.cache,
storage.storedCovers)
storage.storedCovers
)
}
}
@ -70,7 +71,8 @@ private class ExtractStepImpl(
val playlistNodes = filterFlow.left.map { ExtractedMusic.Playlist(it) }
val cacheResults =
audioNodes.map { cache.read(it) }.flowOn(Dispatchers.IO).buffer(Channel.UNLIMITED)
audioNodes.map { wrap(it, cache::read) }.flowOn(Dispatchers.IO)
.buffer(Channel.UNLIMITED)
val cacheFlow =
cacheResults.divert {
when (it) {
@ -84,11 +86,13 @@ private class ExtractStepImpl(
val extractedSongs =
Array(distributedFlow.flows.size) { i ->
distributedFlow.flows[i]
.mapNotNull { file ->
val metadata = metadataExtractor.extract(file) ?: return@mapNotNull null
val tags = tagParser.parse(file, metadata)
val cover = metadata.cover?.let { storedCovers.write(it) }
RawSong(file, metadata.properties, tags, cover)
.mapNotNull { it ->
wrap(it) { file ->
val metadata = metadataExtractor.extract(file) ?: return@wrap null
val tags = tagParser.parse(file, metadata)
val cover = metadata.cover?.let { storedCovers.write(it) }
RawSong(file, metadata.properties, tags, cover)
}
}
.flowOn(Dispatchers.IO)
.buffer(Channel.UNLIMITED)
@ -96,7 +100,7 @@ private class ExtractStepImpl(
val writtenSongs =
merge(*extractedSongs)
.map {
cache.write(it)
wrap(it, cache::write)
ExtractedMusic.Song(it)
}
.flowOn(Dispatchers.IO)
@ -107,7 +111,8 @@ private class ExtractStepImpl(
cachedSongs,
distributedFlow.manager,
writtenSongs,
playlistNodes)
playlistNodes
)
}
}

View file

@ -0,0 +1,72 @@
package org.oxycblt.musikr.pipeline
import org.oxycblt.musikr.fs.DeviceFile
import org.oxycblt.musikr.playlist.PlaylistFile
import org.oxycblt.musikr.playlist.interpret.PrePlaylist
import org.oxycblt.musikr.tag.interpret.PreSong
class PipelineException(
val processing: WhileProcessing,
val error: Exception
) : Exception() {
override val cause = error
override val message = "Error while processing ${processing}: $error"
}
sealed interface WhileProcessing {
class AFile internal constructor(private val file: DeviceFile) : WhileProcessing {
override fun toString() = "File @ ${file.path}"
}
class ARawSong internal constructor(private val rawSong: RawSong) : WhileProcessing {
override fun toString() = "Raw Song @ ${rawSong.file.path}"
}
class APlaylistFile internal constructor(private val playlist: PlaylistFile) : WhileProcessing {
override fun toString() = "Playlist File @ ${playlist.name}"
}
class APreSong internal constructor(private val preSong: PreSong) : WhileProcessing {
override fun toString() = "Pre Song @ ${preSong.path}"
}
class APrePlaylist internal constructor(private val prePlaylist: PrePlaylist) : WhileProcessing {
override fun toString() = "Pre Playlist @ ${prePlaylist.name}"
}
}
internal suspend fun <R> wrap(file: DeviceFile, block: suspend (DeviceFile) -> R): R =
try {
block(file)
} catch (e: Exception) {
throw PipelineException(WhileProcessing.AFile(file), e)
}
internal suspend fun <R> wrap(song: RawSong, block: suspend (RawSong) -> R): R =
try {
block(song)
} catch (e: Exception) {
throw PipelineException(WhileProcessing.ARawSong(song), e)
}
internal suspend fun <R> wrap(file: PlaylistFile, block: suspend (PlaylistFile) -> R): R =
try {
block(file)
} catch (e: Exception) {
throw PipelineException(WhileProcessing.APlaylistFile(file), e)
}
internal suspend fun <R> wrap(song: PreSong, block: suspend (PreSong) -> R): R =
try {
block(song)
} catch (e: Exception) {
throw PipelineException(WhileProcessing.APreSong(song), e)
}
internal suspend fun <R> wrap(playlist: PrePlaylist, block: suspend (PrePlaylist) -> R): R =
try {
block(playlist)
} catch (e: Exception) {
throw PipelineException(WhileProcessing.APrePlaylist(playlist), e)
}