all: temp fix build issues
This commit is contained in:
parent
530d8cc2b5
commit
b53b7a0c6a
9 changed files with 57 additions and 333 deletions
|
@ -37,13 +37,13 @@ class CoilModule {
|
|||
fun imageLoader(
|
||||
@ApplicationContext context: Context,
|
||||
keyer: CoverKeyer,
|
||||
factory: CoverFetcher.Factory
|
||||
// factory: CoverFetcher.Factory
|
||||
) =
|
||||
ImageLoader.Builder(context)
|
||||
.components {
|
||||
// Add fetchers for Music components to make them usable with ImageRequest
|
||||
add(keyer)
|
||||
add(factory)
|
||||
// add(factory)
|
||||
}
|
||||
// Use our own crossfade with error drawable support
|
||||
.transitionFactory(ErrorCrossfadeTransitionFactory())
|
||||
|
|
|
@ -23,14 +23,11 @@ import android.graphics.Bitmap
|
|||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import coil3.ImageLoader
|
||||
import coil3.asImage
|
||||
import coil3.decode.DataSource
|
||||
import coil3.decode.ImageSource
|
||||
import coil3.fetch.FetchResult
|
||||
import coil3.fetch.Fetcher
|
||||
import coil3.fetch.ImageFetchResult
|
||||
import coil3.fetch.SourceFetchResult
|
||||
import coil3.key.Keyer
|
||||
import coil3.request.Options
|
||||
import coil3.size.Dimension
|
||||
|
@ -38,12 +35,6 @@ import coil3.size.Size
|
|||
import coil3.size.pxOrElse
|
||||
import java.io.InputStream
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okio.FileSystem
|
||||
import okio.buffer
|
||||
import okio.source
|
||||
import org.oxycblt.auxio.image.stack.CoverRetriever
|
||||
import org.oxycblt.musikr.cover.Cover
|
||||
|
||||
class CoverKeyer @Inject constructor() : Keyer<Cover> {
|
||||
|
@ -55,46 +46,61 @@ private constructor(
|
|||
private val context: Context,
|
||||
private val cover: Cover,
|
||||
private val size: Size,
|
||||
private val coverRetriever: CoverRetriever,
|
||||
) : Fetcher {
|
||||
override suspend fun fetch(): FetchResult? {
|
||||
val streams =
|
||||
when (val cover = cover) {
|
||||
is Cover.Single -> listOfNotNull(coverRetriever.retrieve(cover))
|
||||
is Cover.Multi ->
|
||||
buildList {
|
||||
for (single in cover.all) {
|
||||
coverRetriever.retrieve(single)?.let { add(it) }
|
||||
if (size == 4) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
// val streams =
|
||||
// when (val cover = cover) {
|
||||
// is Cover.Single -> listOfNotNull(coverRetriever.retrieve(cover))
|
||||
// is Cover.Multi ->
|
||||
// buildList {
|
||||
// for (single in cover.all) {
|
||||
// coverRetriever.retrieve(single)?.let { add(it) }
|
||||
// if (size == 4) {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// We don't immediately check for mosaic feasibility from album count alone, as that
|
||||
// does not factor in InputStreams failing to load. Instead, only check once we
|
||||
// definitely have image data to use.
|
||||
if (streams.size == 4) {
|
||||
// Make sure we free the InputStreams once we've transformed them into a mosaic.
|
||||
return createMosaic(streams, size).also {
|
||||
withContext(Dispatchers.IO) { streams.forEach(InputStream::close) }
|
||||
}
|
||||
}
|
||||
// if (streams.size == 4) {
|
||||
// // Make sure we free the InputStreams once we've transformed them into a
|
||||
// mosaic.
|
||||
// return createMosaic(streams, size).also {
|
||||
// withContext(Dispatchers.IO) { streams.forEach(InputStream::close) }
|
||||
// } // Not enough covers for a mosaic, take the first one (if that even
|
||||
// exists)
|
||||
// val first = streams.firstOrNull() ?: return null
|
||||
//
|
||||
// // All but the first stream will be unused, free their resources
|
||||
// withContext(Dispatchers.IO) {
|
||||
// for (i in 1 until streams.size) {
|
||||
// streams[i].close()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return SourceFetchResult(
|
||||
// source = ImageSource(first.source().buffer(), FileSystem.SYSTEM, null),
|
||||
// mimeType = null,
|
||||
// dataSource = DataSource.DISK)
|
||||
// }
|
||||
|
||||
// Not enough covers for a mosaic, take the first one (if that even exists)
|
||||
val first = streams.firstOrNull() ?: return null
|
||||
|
||||
// All but the first stream will be unused, free their resources
|
||||
withContext(Dispatchers.IO) {
|
||||
for (i in 1 until streams.size) {
|
||||
streams[i].close()
|
||||
}
|
||||
}
|
||||
|
||||
return SourceFetchResult(
|
||||
source = ImageSource(first.source().buffer(), FileSystem.SYSTEM, null),
|
||||
mimeType = null,
|
||||
dataSource = DataSource.DISK)
|
||||
// // Not enough covers for a mosaic, take the first one (if that even exists)
|
||||
// val first = streams.firstOrNull() ?: return null
|
||||
//
|
||||
// // All but the first stream will be unused, free their resources
|
||||
// withContext(Dispatchers.IO) {
|
||||
// for (i in 1 until streams.size) {
|
||||
// streams[i].close()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return SourceFetchResult(
|
||||
// source = ImageSource(first.source().buffer(), FileSystem.SYSTEM, null),
|
||||
// mimeType = null,
|
||||
// dataSource = DataSource.DISK)
|
||||
}
|
||||
|
||||
/** Derived from phonograph: https://github.com/kabouzeid/Phonograph */
|
||||
|
@ -148,9 +154,9 @@ private constructor(
|
|||
return if (size.mod(2) > 0) size + 1 else size
|
||||
}
|
||||
|
||||
class Factory @Inject constructor(private val coverRetriever: CoverRetriever) :
|
||||
Fetcher.Factory<Cover> {
|
||||
override fun create(data: Cover, options: Options, imageLoader: ImageLoader) =
|
||||
CoverFetcher(options.context, data, options.size, coverRetriever)
|
||||
}
|
||||
// class Factory @Inject constructor(private val coverRetriever: CoverRetriever) :
|
||||
// Fetcher.Factory<Cover> {
|
||||
// override fun create(data: Cover, options: Options, imageLoader: ImageLoader) =
|
||||
// CoverFetcher(options.context, data, options.size, coverRetriever)
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* CoverRetriever.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.auxio.image.stack
|
||||
|
||||
import java.io.InputStream
|
||||
import javax.inject.Inject
|
||||
import org.oxycblt.auxio.image.stack.extractor.CoverExtractor
|
||||
import org.oxycblt.musikr.cover.Cover
|
||||
import org.oxycblt.musikr.cover.CoverCache
|
||||
import timber.log.Timber
|
||||
|
||||
interface CoverRetriever {
|
||||
suspend fun retrieve(cover: Cover.Single): InputStream?
|
||||
}
|
||||
|
||||
class CoverRetrieverImpl
|
||||
@Inject
|
||||
constructor(private val coverCache: CoverCache, private val coverExtractor: CoverExtractor) :
|
||||
CoverRetriever {
|
||||
override suspend fun retrieve(cover: Cover.Single) =
|
||||
try {
|
||||
coverCache.read(cover)
|
||||
?: coverExtractor.extract(cover)?.let { coverCache.write(cover, it) }
|
||||
} catch (e: Exception) {
|
||||
Timber.e("Image extraction failed!")
|
||||
Timber.e(e.stackTraceToString())
|
||||
throw e
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* StackModule.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.auxio.image.stack
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface StackModule {
|
||||
@Binds fun coverRetriever(impl: CoverRetrieverImpl): CoverRetriever
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* AOSPCoverSource.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.auxio.image.stack.extractor
|
||||
|
||||
import android.content.Context
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.net.Uri
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class AOSPCoverSource @Inject constructor(@ApplicationContext private val context: Context) :
|
||||
CoverSource {
|
||||
override suspend fun extract(fileUri: Uri): ByteArray? {
|
||||
val mediaMetadataRetriever = MediaMetadataRetriever()
|
||||
return withContext(Dispatchers.IO) {
|
||||
mediaMetadataRetriever.setDataSource(context, fileUri)
|
||||
mediaMetadataRetriever.embeddedPicture
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* CoverExtractor.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.auxio.image.stack.extractor
|
||||
|
||||
import android.net.Uri
|
||||
import javax.inject.Inject
|
||||
import org.oxycblt.musikr.cover.Cover
|
||||
|
||||
interface CoverExtractor {
|
||||
suspend fun extract(cover: Cover.Single): ByteArray?
|
||||
}
|
||||
|
||||
data class CoverSources(val sources: List<CoverSource>)
|
||||
|
||||
interface CoverSource {
|
||||
suspend fun extract(fileUri: Uri): ByteArray?
|
||||
}
|
||||
|
||||
class CoverExtractorImpl @Inject constructor(private val coverSources: CoverSources) :
|
||||
CoverExtractor {
|
||||
override suspend fun extract(cover: Cover.Single): ByteArray? {
|
||||
return null
|
||||
for (coverSource in coverSources.sources) {
|
||||
val stream = coverSource.extract(cover.uri)
|
||||
if (stream != null) {
|
||||
return stream
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* ExoPlayerCoverSource.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.auxio.image.stack.extractor
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.Metadata
|
||||
import androidx.media3.exoplayer.MetadataRetriever
|
||||
import androidx.media3.exoplayer.source.MediaSource
|
||||
import androidx.media3.extractor.metadata.flac.PictureFrame
|
||||
import androidx.media3.extractor.metadata.id3.ApicFrame
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.guava.asDeferred
|
||||
|
||||
class ExoPlayerCoverSource
|
||||
@Inject
|
||||
constructor(private val mediaSourceFactory: MediaSource.Factory) : CoverSource {
|
||||
override suspend fun extract(fileUri: Uri): ByteArray? {
|
||||
val tracks =
|
||||
MetadataRetriever.retrieveMetadata(mediaSourceFactory, MediaItem.fromUri(fileUri))
|
||||
.asDeferred()
|
||||
.await()
|
||||
|
||||
// The metadata extraction process of ExoPlayer results in a dump of all metadata
|
||||
// it found, which must be iterated through.
|
||||
val metadata = tracks[0].getFormat(0).metadata
|
||||
|
||||
if (metadata == null || metadata.length() == 0) {
|
||||
// No (parsable) metadata. This is also expected.
|
||||
return null
|
||||
}
|
||||
|
||||
return findCoverDataInMetadata(metadata)
|
||||
}
|
||||
|
||||
private fun findCoverDataInMetadata(metadata: Metadata): ByteArray? {
|
||||
var fallbackPic: ByteArray? = null
|
||||
|
||||
for (i in 0 until metadata.length()) {
|
||||
// We can only extract pictures from two tags with this method, ID3v2's APIC or
|
||||
// Vorbis picture comments.
|
||||
val pic: ByteArray?
|
||||
val type: Int
|
||||
|
||||
when (val entry = metadata.get(i)) {
|
||||
is ApicFrame -> {
|
||||
pic = entry.pictureData
|
||||
type = entry.pictureType
|
||||
}
|
||||
is PictureFrame -> {
|
||||
pic = entry.pictureData
|
||||
type = entry.pictureType
|
||||
}
|
||||
else -> continue
|
||||
}
|
||||
|
||||
if (type == MediaMetadata.PICTURE_TYPE_FRONT_COVER) {
|
||||
return pic
|
||||
} else if (fallbackPic == null) {
|
||||
fallbackPic = pic
|
||||
}
|
||||
}
|
||||
|
||||
return fallbackPic
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* ExtractorModule.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.auxio.image.stack.extractor
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface ExtractorModule {
|
||||
@Binds fun coverExtractor(impl: CoverExtractorImpl): CoverExtractor
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class CoverSourcesModule {
|
||||
@Provides
|
||||
fun coverSources(exoPlayerCoverSource: ExoPlayerCoverSource, aospCoverSource: AOSPCoverSource) =
|
||||
CoverSources(listOf(aospCoverSource, exoPlayerCoverSource))
|
||||
}
|
|
@ -56,7 +56,7 @@ class SongImpl(private val handle: SongCore) : Song {
|
|||
override val replayGainAdjustment = preSong.replayGainAdjustment
|
||||
override val lastModified = preSong.lastModified
|
||||
override val dateAdded = preSong.dateAdded
|
||||
override val cover = Cover.single(this)
|
||||
override val cover = Cover.single("")
|
||||
override val album: Album
|
||||
get() = handle.resolveAlbum()
|
||||
|
||||
|
|
Loading…
Reference in a new issue