all: temp fix build issues

This commit is contained in:
Alexander Capehart 2024-12-11 13:18:22 -07:00
parent 530d8cc2b5
commit b53b7a0c6a
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
9 changed files with 57 additions and 333 deletions

View file

@ -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())

View file

@ -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)
} // }
} }

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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
}
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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))
}

View file

@ -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()