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(
|
fun imageLoader(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
keyer: CoverKeyer,
|
keyer: CoverKeyer,
|
||||||
factory: CoverFetcher.Factory
|
// factory: CoverFetcher.Factory
|
||||||
) =
|
) =
|
||||||
ImageLoader.Builder(context)
|
ImageLoader.Builder(context)
|
||||||
.components {
|
.components {
|
||||||
// Add fetchers for Music components to make them usable with ImageRequest
|
// Add fetchers for Music components to make them usable with ImageRequest
|
||||||
add(keyer)
|
add(keyer)
|
||||||
add(factory)
|
// add(factory)
|
||||||
}
|
}
|
||||||
// Use our own crossfade with error drawable support
|
// Use our own crossfade with error drawable support
|
||||||
.transitionFactory(ErrorCrossfadeTransitionFactory())
|
.transitionFactory(ErrorCrossfadeTransitionFactory())
|
||||||
|
|
|
@ -23,14 +23,11 @@ import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
import coil3.ImageLoader
|
|
||||||
import coil3.asImage
|
import coil3.asImage
|
||||||
import coil3.decode.DataSource
|
import coil3.decode.DataSource
|
||||||
import coil3.decode.ImageSource
|
|
||||||
import coil3.fetch.FetchResult
|
import coil3.fetch.FetchResult
|
||||||
import coil3.fetch.Fetcher
|
import coil3.fetch.Fetcher
|
||||||
import coil3.fetch.ImageFetchResult
|
import coil3.fetch.ImageFetchResult
|
||||||
import coil3.fetch.SourceFetchResult
|
|
||||||
import coil3.key.Keyer
|
import coil3.key.Keyer
|
||||||
import coil3.request.Options
|
import coil3.request.Options
|
||||||
import coil3.size.Dimension
|
import coil3.size.Dimension
|
||||||
|
@ -38,12 +35,6 @@ import coil3.size.Size
|
||||||
import coil3.size.pxOrElse
|
import coil3.size.pxOrElse
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import javax.inject.Inject
|
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
|
import org.oxycblt.musikr.cover.Cover
|
||||||
|
|
||||||
class CoverKeyer @Inject constructor() : Keyer<Cover> {
|
class CoverKeyer @Inject constructor() : Keyer<Cover> {
|
||||||
|
@ -55,46 +46,61 @@ private constructor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val cover: Cover,
|
private val cover: Cover,
|
||||||
private val size: Size,
|
private val size: Size,
|
||||||
private val coverRetriever: CoverRetriever,
|
|
||||||
) : Fetcher {
|
) : Fetcher {
|
||||||
override suspend fun fetch(): FetchResult? {
|
override suspend fun fetch(): FetchResult? {
|
||||||
val streams =
|
return null
|
||||||
when (val cover = cover) {
|
// val streams =
|
||||||
is Cover.Single -> listOfNotNull(coverRetriever.retrieve(cover))
|
// when (val cover = cover) {
|
||||||
is Cover.Multi ->
|
// is Cover.Single -> listOfNotNull(coverRetriever.retrieve(cover))
|
||||||
buildList {
|
// is Cover.Multi ->
|
||||||
for (single in cover.all) {
|
// buildList {
|
||||||
coverRetriever.retrieve(single)?.let { add(it) }
|
// for (single in cover.all) {
|
||||||
if (size == 4) {
|
// coverRetriever.retrieve(single)?.let { add(it) }
|
||||||
break
|
// if (size == 4) {
|
||||||
}
|
// break
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
// We don't immediately check for mosaic feasibility from album count alone, as that
|
// 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
|
// does not factor in InputStreams failing to load. Instead, only check once we
|
||||||
// definitely have image data to use.
|
// definitely have image data to use.
|
||||||
if (streams.size == 4) {
|
// if (streams.size == 4) {
|
||||||
// Make sure we free the InputStreams once we've transformed them into a mosaic.
|
// // Make sure we free the InputStreams once we've transformed them into a
|
||||||
return createMosaic(streams, size).also {
|
// mosaic.
|
||||||
withContext(Dispatchers.IO) { streams.forEach(InputStream::close) }
|
// 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)
|
// // Not enough covers for a mosaic, take the first one (if that even exists)
|
||||||
val first = streams.firstOrNull() ?: return null
|
// val first = streams.firstOrNull() ?: return null
|
||||||
|
//
|
||||||
// All but the first stream will be unused, free their resources
|
// // All but the first stream will be unused, free their resources
|
||||||
withContext(Dispatchers.IO) {
|
// withContext(Dispatchers.IO) {
|
||||||
for (i in 1 until streams.size) {
|
// for (i in 1 until streams.size) {
|
||||||
streams[i].close()
|
// streams[i].close()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return SourceFetchResult(
|
// return SourceFetchResult(
|
||||||
source = ImageSource(first.source().buffer(), FileSystem.SYSTEM, null),
|
// source = ImageSource(first.source().buffer(), FileSystem.SYSTEM, null),
|
||||||
mimeType = null,
|
// mimeType = null,
|
||||||
dataSource = DataSource.DISK)
|
// dataSource = DataSource.DISK)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Derived from phonograph: https://github.com/kabouzeid/Phonograph */
|
/** Derived from phonograph: https://github.com/kabouzeid/Phonograph */
|
||||||
|
@ -148,9 +154,9 @@ private constructor(
|
||||||
return if (size.mod(2) > 0) size + 1 else size
|
return if (size.mod(2) > 0) size + 1 else size
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory @Inject constructor(private val coverRetriever: CoverRetriever) :
|
// class Factory @Inject constructor(private val coverRetriever: CoverRetriever) :
|
||||||
Fetcher.Factory<Cover> {
|
// Fetcher.Factory<Cover> {
|
||||||
override fun create(data: Cover, options: Options, imageLoader: ImageLoader) =
|
// override fun create(data: Cover, options: Options, imageLoader: ImageLoader) =
|
||||||
CoverFetcher(options.context, data, options.size, coverRetriever)
|
// 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 replayGainAdjustment = preSong.replayGainAdjustment
|
||||||
override val lastModified = preSong.lastModified
|
override val lastModified = preSong.lastModified
|
||||||
override val dateAdded = preSong.dateAdded
|
override val dateAdded = preSong.dateAdded
|
||||||
override val cover = Cover.single(this)
|
override val cover = Cover.single("")
|
||||||
override val album: Album
|
override val album: Album
|
||||||
get() = handle.resolveAlbum()
|
get() = handle.resolveAlbum()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue