mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Merge branch 'main' into fea/music
This commit is contained in:
commit
1ee1a855a2
22 changed files with 1128 additions and 406 deletions
|
|
@ -93,9 +93,6 @@ class MainActivity : AppCompatActivity() {
|
|||
@Inject
|
||||
lateinit var updateChecker: UpdateChecker
|
||||
|
||||
@Inject
|
||||
lateinit var appUpgradeHandler: AppUpgradeHandler
|
||||
|
||||
@Inject
|
||||
lateinit var playbackLifecycleObserver: PlaybackLifecycleObserver
|
||||
|
||||
|
|
@ -136,11 +133,7 @@ class MainActivity : AppCompatActivity() {
|
|||
instance = this
|
||||
Timber.i("MainActivity.onCreate: savedInstanceState is null=${savedInstanceState == null}")
|
||||
lifecycle.addObserver(playbackLifecycleObserver)
|
||||
if (savedInstanceState == null) {
|
||||
lifecycleScope.launchIO {
|
||||
appUpgradeHandler.copySubfont(false)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.serverRepository.currentUser.observe(this) { user ->
|
||||
if (user?.hasPin == true) {
|
||||
window?.setFlags(
|
||||
|
|
@ -249,7 +242,6 @@ class MainActivity : AppCompatActivity() {
|
|||
Timber.d("onResume")
|
||||
lifecycleScope.launchDefault {
|
||||
screensaverService.pulse()
|
||||
appUpgradeHandler.run()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -384,10 +376,13 @@ class MainActivityViewModel
|
|||
private val navigationManager: SetupNavigationManager,
|
||||
private val deviceProfileService: DeviceProfileService,
|
||||
private val backdropService: BackdropService,
|
||||
private val appUpgradeHandler: AppUpgradeHandler,
|
||||
) : ViewModel() {
|
||||
fun appStart() {
|
||||
viewModelScope.launchIO {
|
||||
try {
|
||||
appUpgradeHandler.run()
|
||||
appUpgradeHandler.copySubfont(false)
|
||||
val prefs =
|
||||
preferences.data.firstOrNull() ?: AppPreferences.getDefaultInstance()
|
||||
val userHasPin = serverRepository.currentUser.value?.hasPin == true
|
||||
|
|
|
|||
|
|
@ -3,25 +3,16 @@
|
|||
package com.github.damontecres.wholphin.data.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.github.damontecres.wholphin.api.seerr.model.CreditCast
|
||||
import com.github.damontecres.wholphin.api.seerr.model.CreditCrew
|
||||
import com.github.damontecres.wholphin.api.seerr.model.MovieDetails
|
||||
import com.github.damontecres.wholphin.api.seerr.model.MovieMovieIdRatingsGet200Response
|
||||
import com.github.damontecres.wholphin.api.seerr.model.MovieResult
|
||||
import com.github.damontecres.wholphin.api.seerr.model.TvDetails
|
||||
import com.github.damontecres.wholphin.api.seerr.model.TvResult
|
||||
import com.github.damontecres.wholphin.api.seerr.model.TvTvIdRatingsGet200Response
|
||||
import com.github.damontecres.wholphin.services.SeerrSearchResult
|
||||
import com.github.damontecres.wholphin.ui.detail.CardGridItem
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.toLocalDate
|
||||
import com.github.damontecres.wholphin.util.LocalDateSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.serializer.UUIDSerializer
|
||||
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
||||
import java.time.LocalDate
|
||||
import java.util.UUID
|
||||
|
||||
|
|
@ -85,17 +76,14 @@ data class DiscoverItem(
|
|||
val overview: String?,
|
||||
val availability: SeerrAvailability,
|
||||
@Serializable(LocalDateSerializer::class) val releaseDate: LocalDate?,
|
||||
val posterPath: String?,
|
||||
val backdropPath: String?,
|
||||
val posterUrl: String?,
|
||||
val backDropUrl: String?,
|
||||
val jellyfinItemId: UUID?,
|
||||
) : CardGridItem {
|
||||
override val gridId: String get() = id.toString()
|
||||
override val playable: Boolean = false
|
||||
override val sortName: String get() = title ?: ""
|
||||
|
||||
val backDropUrl: String? get() = backdropPath?.let { "https://image.tmdb.org/t/p/w1920_and_h1080_multi_faces$it" }
|
||||
val posterUrl: String? get() = posterPath?.let { "https://image.tmdb.org/t/p/w500$it" }
|
||||
|
||||
val destination: Destination
|
||||
get() {
|
||||
val jfType =
|
||||
|
|
@ -114,103 +102,6 @@ data class DiscoverItem(
|
|||
Destination.DiscoveredItem(this)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(movie: MovieResult) : this(
|
||||
id = movie.id,
|
||||
type = SeerrItemType.MOVIE,
|
||||
title = movie.title,
|
||||
subtitle = null,
|
||||
overview = movie.overview,
|
||||
availability = SeerrAvailability.from(movie.mediaInfo?.status) ?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(movie.releaseDate),
|
||||
posterPath = movie.posterPath,
|
||||
backdropPath = movie.backdropPath,
|
||||
jellyfinItemId = movie.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
constructor(movie: MovieDetails) : this(
|
||||
id = movie.id ?: -1,
|
||||
type = SeerrItemType.MOVIE,
|
||||
title = movie.title,
|
||||
subtitle = null,
|
||||
overview = movie.overview,
|
||||
availability = SeerrAvailability.from(movie.mediaInfo?.status) ?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(movie.releaseDate),
|
||||
posterPath = movie.posterPath,
|
||||
backdropPath = movie.backdropPath,
|
||||
jellyfinItemId = movie.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
constructor(tv: TvResult) : this(
|
||||
id = tv.id!!,
|
||||
type = SeerrItemType.TV,
|
||||
title = tv.name,
|
||||
subtitle = null,
|
||||
overview = tv.overview,
|
||||
availability = SeerrAvailability.from(tv.mediaInfo?.status) ?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(tv.firstAirDate),
|
||||
posterPath = tv.posterPath,
|
||||
backdropPath = tv.backdropPath,
|
||||
jellyfinItemId = tv.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
constructor(tv: TvDetails) : this(
|
||||
id = tv.id!!,
|
||||
type = SeerrItemType.TV,
|
||||
title = tv.name,
|
||||
subtitle = null,
|
||||
overview = tv.overview,
|
||||
availability = SeerrAvailability.from(tv.mediaInfo?.status) ?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(tv.firstAirDate),
|
||||
posterPath = tv.posterPath,
|
||||
backdropPath = tv.backdropPath,
|
||||
jellyfinItemId = tv.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
constructor(search: SeerrSearchResult) : this(
|
||||
id = search.id,
|
||||
type = SeerrItemType.fromString(search.mediaType),
|
||||
title = search.title ?: search.name,
|
||||
subtitle = null,
|
||||
overview = search.overview,
|
||||
availability =
|
||||
SeerrAvailability.from(search.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(search.releaseDate ?: search.firstAirDate),
|
||||
posterPath = search.posterPath,
|
||||
backdropPath = search.backdropPath,
|
||||
jellyfinItemId = search.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
constructor(credit: CreditCast) : this(
|
||||
id = credit.id!!,
|
||||
type = SeerrItemType.fromString(credit.mediaType, SeerrItemType.PERSON),
|
||||
title = credit.name ?: credit.title,
|
||||
subtitle = credit.character,
|
||||
overview = credit.overview,
|
||||
availability =
|
||||
SeerrAvailability.from(credit.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(credit.firstAirDate),
|
||||
posterPath = credit.posterPath ?: credit.profilePath,
|
||||
backdropPath = credit.backdropPath,
|
||||
jellyfinItemId = credit.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
constructor(credit: CreditCrew) : this(
|
||||
id = credit.id!!,
|
||||
type = SeerrItemType.fromString(credit.mediaType, SeerrItemType.PERSON),
|
||||
title = credit.name ?: credit.title,
|
||||
subtitle = credit.job,
|
||||
overview = credit.overview,
|
||||
availability =
|
||||
SeerrAvailability.from(credit.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(credit.firstAirDate),
|
||||
posterPath = credit.posterPath ?: credit.profilePath,
|
||||
backdropPath = credit.backdropPath,
|
||||
jellyfinItemId = credit.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
}
|
||||
|
||||
data class DiscoverRating(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import androidx.core.content.edit
|
|||
import androidx.datastore.core.DataStore
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.github.damontecres.wholphin.WholphinApplication
|
||||
import com.github.damontecres.wholphin.data.SeerrServerDao
|
||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.preferences.ScreensaverPreference
|
||||
|
|
@ -22,6 +23,7 @@ import com.github.damontecres.wholphin.preferences.updateScreensaverPreferences
|
|||
import com.github.damontecres.wholphin.preferences.updateSubtitlePreferences
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings
|
||||
import com.github.damontecres.wholphin.ui.setup.seerr.migrateSeerrUrl
|
||||
import com.github.damontecres.wholphin.util.Version
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
|
|
@ -36,9 +38,14 @@ class AppUpgradeHandler
|
|||
constructor(
|
||||
@param:ApplicationContext private val context: Context,
|
||||
private val appPreferences: DataStore<AppPreferences>,
|
||||
private val seerrServerDao: SeerrServerDao,
|
||||
) {
|
||||
suspend fun run() {
|
||||
val pkgInfo = WholphinApplication.instance.packageManager.getPackageInfo(WholphinApplication.instance.packageName, 0)
|
||||
val pkgInfo =
|
||||
WholphinApplication.instance.packageManager.getPackageInfo(
|
||||
WholphinApplication.instance.packageName,
|
||||
0,
|
||||
)
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val previousVersion = prefs.getString(VERSION_NAME_CURRENT_KEY, null)
|
||||
val previousVersionCode = prefs.getLong(VERSION_CODE_CURRENT_KEY, -1)
|
||||
|
|
@ -63,14 +70,16 @@ class AppUpgradeHandler
|
|||
try {
|
||||
copySubfont(true)
|
||||
upgradeApp(
|
||||
context,
|
||||
Version.Companion.fromString(previousVersion ?: "0.0.0"),
|
||||
Version.Companion.fromString(newVersion),
|
||||
Version.fromString(previousVersion ?: "0.0.0"),
|
||||
Version.fromString(newVersion),
|
||||
appPreferences,
|
||||
)
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Exception during app upgrade")
|
||||
}
|
||||
Timber.i("App upgrade complete")
|
||||
} else {
|
||||
Timber.d("No app update needed")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -101,10 +110,8 @@ class AppUpgradeHandler
|
|||
const val VERSION_NAME_CURRENT_KEY = "version.current.name"
|
||||
const val VERSION_CODE_CURRENT_KEY = "version.current.code"
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun upgradeApp(
|
||||
context: Context,
|
||||
previous: Version,
|
||||
current: Version,
|
||||
appPreferences: DataStore<AppPreferences>,
|
||||
|
|
@ -227,7 +234,8 @@ suspend fun upgradeApp(
|
|||
subtitlesPreferences
|
||||
.toBuilder()
|
||||
.apply {
|
||||
imageSubtitleOpacity = SubtitleSettings.ImageOpacity.defaultValue.toInt()
|
||||
imageSubtitleOpacity =
|
||||
SubtitleSettings.ImageOpacity.defaultValue.toInt()
|
||||
}.build()
|
||||
// Copy current subtitle prefs as HDR ones
|
||||
hdrSubtitlesPreferences = subtitlesPreferences.toBuilder().build()
|
||||
|
|
@ -265,7 +273,16 @@ suspend fun upgradeApp(
|
|||
}
|
||||
}
|
||||
|
||||
if (previous.isEqualOrBefore(Version.fromString("0.5.3-1-g0"))) {
|
||||
if (previous.isEqualOrBefore(Version.fromString("0.5.4-0-g0"))) {
|
||||
seerrServerDao.getServers().forEach {
|
||||
val server = it.server
|
||||
seerrServerDao.updateServer(
|
||||
server.copy(url = migrateSeerrUrl(server.url)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (previous.isEqualOrBefore(Version.fromString("0.6.0-0-g0"))) {
|
||||
appPreferences.updateData {
|
||||
it.updateMusicPreferences {
|
||||
showBackdrop = true
|
||||
|
|
@ -275,3 +292,4 @@ suspend fun upgradeApp(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.github.damontecres.wholphin.services
|
|||
|
||||
import com.github.damontecres.wholphin.api.seerr.SeerrApiClient
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.setup.seerr.createSeerrApiUrl
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
/**
|
||||
|
|
@ -24,6 +25,6 @@ class SeerrApi(
|
|||
baseUrl: String,
|
||||
apiKey: String?,
|
||||
) {
|
||||
api = SeerrApiClient(baseUrl, apiKey, okHttpClient)
|
||||
api = SeerrApiClient(createSeerrApiUrl(baseUrl), apiKey, okHttpClient)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.github.damontecres.wholphin.data.model.SeerrUser
|
|||
import com.github.damontecres.wholphin.data.model.hasPermission
|
||||
import com.github.damontecres.wholphin.services.hilt.StandardOkHttpClient
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.setup.seerr.createSeerrApiUrl
|
||||
import com.github.damontecres.wholphin.util.LoadingState
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
|
|
@ -30,6 +31,7 @@ import kotlinx.coroutines.flow.map
|
|||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import okhttp3.OkHttpClient
|
||||
import org.jellyfin.sdk.model.api.ImageType
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
|
@ -163,7 +165,7 @@ class SeerrServerRepository
|
|||
val apiKey = passwordOrApiKey.takeIf { authMethod == SeerrAuthMethod.API_KEY }
|
||||
val api =
|
||||
SeerrApiClient(
|
||||
url,
|
||||
createSeerrApiUrl(url),
|
||||
apiKey,
|
||||
okHttpClient
|
||||
.newBuilder()
|
||||
|
|
@ -331,3 +333,24 @@ class UserSwitchListener
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun CurrentSeerr?.imageUrlBuilder(
|
||||
imageType: ImageType,
|
||||
path: String?,
|
||||
): String? {
|
||||
if (this == null) return null
|
||||
val cacheImages = serverConfig.cacheImages == true
|
||||
val base =
|
||||
if (cacheImages) {
|
||||
server.url.removeSuffix("/") + "/imageproxy/tmdb"
|
||||
} else {
|
||||
"https://image.tmdb.org"
|
||||
}
|
||||
val prefix =
|
||||
when (imageType) {
|
||||
ImageType.PRIMARY -> "/t/p/w500"
|
||||
ImageType.BACKDROP -> "/t/p/w1920_and_h1080_multi_faces"
|
||||
else -> throw IllegalArgumentException("Image type not supported: $imageType")
|
||||
}
|
||||
return "${base}${prefix}$path"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,25 @@
|
|||
package com.github.damontecres.wholphin.services
|
||||
|
||||
import com.github.damontecres.wholphin.api.seerr.SeerrApiClient
|
||||
import com.github.damontecres.wholphin.api.seerr.model.CreditCast
|
||||
import com.github.damontecres.wholphin.api.seerr.model.CreditCrew
|
||||
import com.github.damontecres.wholphin.api.seerr.model.MediaInfo
|
||||
import com.github.damontecres.wholphin.api.seerr.model.MovieDetails
|
||||
import com.github.damontecres.wholphin.api.seerr.model.MovieResult
|
||||
import com.github.damontecres.wholphin.api.seerr.model.SearchGet200ResponseResultsInner
|
||||
import com.github.damontecres.wholphin.api.seerr.model.TvDetails
|
||||
import com.github.damontecres.wholphin.api.seerr.model.TvResult
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.data.model.DiscoverItem
|
||||
import com.github.damontecres.wholphin.data.model.SeerrAvailability
|
||||
import com.github.damontecres.wholphin.data.model.SeerrItemType
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.toLocalDate
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.api.ImageType
|
||||
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -23,6 +36,7 @@ class SeerrService
|
|||
constructor(
|
||||
private val seerApi: SeerrApi,
|
||||
private val seerrServerRepository: SeerrServerRepository,
|
||||
private val imageUrlService: ImageUrlService,
|
||||
) {
|
||||
val api: SeerrApiClient get() = seerApi.api
|
||||
|
||||
|
|
@ -41,35 +55,35 @@ class SeerrService
|
|||
api.searchApi
|
||||
.discoverTvGet(page = page)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
|
||||
suspend fun discoverMovies(page: Int = 1): List<DiscoverItem> =
|
||||
api.searchApi
|
||||
.discoverMoviesGet(page = page)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
|
||||
suspend fun trending(page: Int = 1): List<DiscoverItem> =
|
||||
api.searchApi
|
||||
.discoverTrendingGet(page = page)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
|
||||
suspend fun upcomingMovies(page: Int = 1): List<DiscoverItem> =
|
||||
api.searchApi
|
||||
.discoverMoviesUpcomingGet(page = page)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
|
||||
suspend fun upcomingTv(page: Int = 1): List<DiscoverItem> =
|
||||
api.searchApi
|
||||
.discoverTvUpcomingGet(page = page)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
|
||||
/**
|
||||
|
|
@ -90,14 +104,14 @@ class SeerrService
|
|||
api.moviesApi
|
||||
.movieMovieIdSimilarGet(movieId = it)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { createDiscoverItem(it) }
|
||||
}
|
||||
|
||||
BaseItemKind.SERIES, BaseItemKind.SEASON, BaseItemKind.EPISODE -> {
|
||||
api.tvApi
|
||||
.tvTvIdSimilarGet(tvId = it)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { createDiscoverItem(it) }
|
||||
}
|
||||
|
||||
BaseItemKind.PERSON -> {
|
||||
|
|
@ -107,12 +121,12 @@ class SeerrService
|
|||
val cast =
|
||||
credits.cast
|
||||
?.take(25)
|
||||
?.map(::DiscoverItem)
|
||||
?.map { createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
val crew =
|
||||
credits.crew
|
||||
?.take(25)
|
||||
?.map(::DiscoverItem)
|
||||
?.map { createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
cast + crew
|
||||
}
|
||||
|
|
@ -138,4 +152,174 @@ class SeerrService
|
|||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
private suspend fun createImageUrl(
|
||||
imageType: ImageType,
|
||||
path: String?,
|
||||
mediaInfo: MediaInfo?,
|
||||
): String? {
|
||||
if (mediaInfo != null) {
|
||||
val itemId =
|
||||
if (mediaInfo.jellyfinMediaId.isNotNullOrBlank()) {
|
||||
mediaInfo.jellyfinMediaId.toUUIDOrNull()
|
||||
} else if (mediaInfo.jellyfinMediaId4k.isNotNullOrBlank()) {
|
||||
mediaInfo.jellyfinMediaId4k.toUUIDOrNull()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (itemId != null) {
|
||||
return imageUrlService.getItemImageUrl(
|
||||
itemId = itemId,
|
||||
imageType = imageType,
|
||||
)
|
||||
}
|
||||
}
|
||||
val current = seerrServerRepository.current.firstOrNull() ?: return null
|
||||
val cacheImages = current.serverConfig.cacheImages == true
|
||||
val base =
|
||||
if (cacheImages) {
|
||||
current.server.url.removeSuffix("/") + "/imageproxy/tmdb"
|
||||
} else {
|
||||
"https://image.tmdb.org"
|
||||
}
|
||||
val prefix =
|
||||
when (imageType) {
|
||||
ImageType.PRIMARY -> "/t/p/w500"
|
||||
ImageType.BACKDROP -> "/t/p/w1920_and_h1080_multi_faces"
|
||||
else -> throw IllegalArgumentException("Image type not supported: $imageType")
|
||||
}
|
||||
return "${base}${prefix}$path"
|
||||
}
|
||||
|
||||
suspend fun createDiscoverItem(movie: MovieResult): DiscoverItem =
|
||||
DiscoverItem(
|
||||
id = movie.id,
|
||||
type = SeerrItemType.MOVIE,
|
||||
title = movie.title,
|
||||
subtitle = null,
|
||||
overview = movie.overview,
|
||||
availability =
|
||||
SeerrAvailability.from(movie.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(movie.releaseDate),
|
||||
posterUrl = createImageUrl(ImageType.PRIMARY, movie.posterPath, movie.mediaInfo),
|
||||
backDropUrl = createImageUrl(ImageType.BACKDROP, movie.backdropPath, movie.mediaInfo),
|
||||
jellyfinItemId = movie.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
suspend fun createDiscoverItem(movie: MovieDetails): DiscoverItem =
|
||||
DiscoverItem(
|
||||
id = movie.id ?: -1,
|
||||
type = SeerrItemType.MOVIE,
|
||||
title = movie.title,
|
||||
subtitle = null,
|
||||
overview = movie.overview,
|
||||
availability =
|
||||
SeerrAvailability.from(movie.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(movie.releaseDate),
|
||||
posterUrl = createImageUrl(ImageType.PRIMARY, movie.posterPath, movie.mediaInfo),
|
||||
backDropUrl = createImageUrl(ImageType.BACKDROP, movie.backdropPath, movie.mediaInfo),
|
||||
jellyfinItemId = movie.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
suspend fun createDiscoverItem(tv: TvResult): DiscoverItem =
|
||||
DiscoverItem(
|
||||
id = tv.id!!,
|
||||
type = SeerrItemType.TV,
|
||||
title = tv.name,
|
||||
subtitle = null,
|
||||
overview = tv.overview,
|
||||
availability =
|
||||
SeerrAvailability.from(tv.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(tv.firstAirDate),
|
||||
posterUrl = createImageUrl(ImageType.PRIMARY, tv.posterPath, tv.mediaInfo),
|
||||
backDropUrl = createImageUrl(ImageType.BACKDROP, tv.backdropPath, tv.mediaInfo),
|
||||
jellyfinItemId = tv.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
suspend fun createDiscoverItem(tv: TvDetails): DiscoverItem =
|
||||
DiscoverItem(
|
||||
id = tv.id!!,
|
||||
type = SeerrItemType.TV,
|
||||
title = tv.name,
|
||||
subtitle = null,
|
||||
overview = tv.overview,
|
||||
availability =
|
||||
SeerrAvailability.from(tv.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(tv.firstAirDate),
|
||||
posterUrl = createImageUrl(ImageType.PRIMARY, tv.posterPath, tv.mediaInfo),
|
||||
backDropUrl = createImageUrl(ImageType.BACKDROP, tv.backdropPath, tv.mediaInfo),
|
||||
jellyfinItemId = tv.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
suspend fun createDiscoverItem(search: SeerrSearchResult): DiscoverItem =
|
||||
DiscoverItem(
|
||||
id = search.id,
|
||||
type = SeerrItemType.fromString(search.mediaType),
|
||||
title = search.title ?: search.name,
|
||||
subtitle = null,
|
||||
overview = search.overview,
|
||||
availability =
|
||||
SeerrAvailability.from(search.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(search.releaseDate ?: search.firstAirDate),
|
||||
posterUrl = createImageUrl(ImageType.PRIMARY, search.posterPath, search.mediaInfo),
|
||||
backDropUrl = createImageUrl(ImageType.BACKDROP, search.backdropPath, search.mediaInfo),
|
||||
jellyfinItemId = search.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
suspend fun createDiscoverItem(credit: CreditCast): DiscoverItem =
|
||||
DiscoverItem(
|
||||
id = credit.id!!,
|
||||
type = SeerrItemType.fromString(credit.mediaType, SeerrItemType.PERSON),
|
||||
title = credit.name ?: credit.title,
|
||||
subtitle = credit.character,
|
||||
overview = credit.overview,
|
||||
availability =
|
||||
SeerrAvailability.from(credit.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(credit.firstAirDate),
|
||||
posterUrl =
|
||||
createImageUrl(
|
||||
ImageType.PRIMARY,
|
||||
credit.posterPath ?: credit.profilePath,
|
||||
credit.mediaInfo,
|
||||
),
|
||||
backDropUrl =
|
||||
createImageUrl(
|
||||
ImageType.BACKDROP,
|
||||
credit.backdropPath,
|
||||
credit.mediaInfo,
|
||||
),
|
||||
jellyfinItemId = credit.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
|
||||
suspend fun createDiscoverItem(credit: CreditCrew): DiscoverItem =
|
||||
DiscoverItem(
|
||||
id = credit.id!!,
|
||||
type = SeerrItemType.fromString(credit.mediaType, SeerrItemType.PERSON),
|
||||
title = credit.name ?: credit.title,
|
||||
subtitle = credit.job,
|
||||
overview = credit.overview,
|
||||
availability =
|
||||
SeerrAvailability.from(credit.mediaInfo?.status)
|
||||
?: SeerrAvailability.UNKNOWN,
|
||||
releaseDate = toLocalDate(credit.firstAirDate),
|
||||
posterUrl =
|
||||
createImageUrl(
|
||||
ImageType.PRIMARY,
|
||||
credit.posterPath ?: credit.profilePath,
|
||||
credit.mediaInfo,
|
||||
),
|
||||
backDropUrl =
|
||||
createImageUrl(
|
||||
ImageType.BACKDROP,
|
||||
credit.backdropPath,
|
||||
credit.mediaInfo,
|
||||
),
|
||||
jellyfinItemId = credit.mediaInfo?.jellyfinMediaId?.toUUIDOrNull(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.core.content.FileProvider
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.github.damontecres.wholphin.BuildConfig
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.services.UpdateChecker.Companion.ASSET_NAME
|
||||
import com.github.damontecres.wholphin.services.hilt.StandardOkHttpClient
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.showToast
|
||||
|
|
@ -51,9 +53,8 @@ class UpdateChecker
|
|||
@param:StandardOkHttpClient private val okHttpClient: OkHttpClient,
|
||||
) {
|
||||
companion object {
|
||||
// TODO apk names
|
||||
private const val ASSET_NAME = "Wholphin"
|
||||
private const val APK_NAME = "$ASSET_NAME.apk"
|
||||
const val ASSET_NAME = "Wholphin"
|
||||
const val APK_NAME = "$ASSET_NAME.apk"
|
||||
|
||||
private const val APK_MIME_TYPE = "application/vnd.android.package-archive"
|
||||
|
||||
|
|
@ -118,11 +119,6 @@ class UpdateChecker
|
|||
|
||||
suspend fun getLatestRelease(updateUrl: String): Release? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val preferRelease =
|
||||
PreferenceManager
|
||||
.getDefaultSharedPreferences(context)
|
||||
.getBoolean("updatePreferRelease", true)
|
||||
|
||||
val request =
|
||||
Request
|
||||
.Builder()
|
||||
|
|
@ -140,7 +136,7 @@ class UpdateChecker
|
|||
val downloadUrl =
|
||||
result.jsonObject["assets"]
|
||||
?.jsonArray
|
||||
?.let { assets -> getDownloadUrl(assets, preferRelease) }
|
||||
?.let { assets -> getDownloadUrl(assets, BuildConfig.DEBUG) }
|
||||
Timber.v("version=$version, downloadUrl=$downloadUrl")
|
||||
if (version != null) {
|
||||
val notes =
|
||||
|
|
@ -165,35 +161,6 @@ class UpdateChecker
|
|||
}
|
||||
}
|
||||
|
||||
private fun getDownloadUrl(
|
||||
assets: JsonArray,
|
||||
preferRelease: Boolean,
|
||||
): String? {
|
||||
val abiSuffix = Build.SUPPORTED_ABIS.firstOrNull().let { if (it != null) "-$it" else "" }
|
||||
val releaseSuffix = if (preferRelease) "-release" else "-debug"
|
||||
val preferredNames =
|
||||
listOf(
|
||||
"$ASSET_NAME${releaseSuffix}$abiSuffix.apk",
|
||||
"$ASSET_NAME$releaseSuffix.apk",
|
||||
"$ASSET_NAME.apk",
|
||||
)
|
||||
var preferredAsset: JsonObject? = null
|
||||
outer@ for (name in preferredNames) {
|
||||
for (asset in assets) {
|
||||
val assetName =
|
||||
asset.jsonObject["name"]?.jsonPrimitive?.contentOrNull
|
||||
if (name == assetName) {
|
||||
preferredAsset = asset.jsonObject
|
||||
break@outer
|
||||
}
|
||||
}
|
||||
}
|
||||
return preferredAsset
|
||||
?.get("browser_download_url")
|
||||
?.jsonPrimitive
|
||||
?.contentOrNull
|
||||
}
|
||||
|
||||
suspend fun installRelease(
|
||||
release: Release,
|
||||
callback: DownloadCallback,
|
||||
|
|
@ -387,3 +354,33 @@ suspend fun copyTo(
|
|||
}
|
||||
return bytesCopied
|
||||
}
|
||||
|
||||
fun getDownloadUrl(
|
||||
assets: JsonArray,
|
||||
debug: Boolean,
|
||||
supportedAbis: List<String> = Build.SUPPORTED_ABIS.toList(),
|
||||
): String? {
|
||||
val abiSuffix = supportedAbis.firstOrNull().let { if (it != null) "-$it" else "" }
|
||||
val releaseSuffix = if (debug) "-debug" else "-release"
|
||||
val preferredNames =
|
||||
buildList {
|
||||
add("$ASSET_NAME${releaseSuffix}$abiSuffix.apk")
|
||||
add("$ASSET_NAME$releaseSuffix.apk")
|
||||
if (!debug) add("$ASSET_NAME.apk")
|
||||
}
|
||||
var preferredAsset: JsonObject? = null
|
||||
outer@ for (name in preferredNames) {
|
||||
for (asset in assets) {
|
||||
val assetName =
|
||||
asset.jsonObject["name"]?.jsonPrimitive?.contentOrNull
|
||||
if (name == assetName) {
|
||||
preferredAsset = asset.jsonObject
|
||||
break@outer
|
||||
}
|
||||
}
|
||||
}
|
||||
return preferredAsset
|
||||
?.get("browser_download_url")
|
||||
?.jsonPrimitive
|
||||
?.contentOrNull
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class DiscoverMovieViewModel
|
|||
) {
|
||||
Timber.v("Init for movie %s", item.id)
|
||||
val movie = fetchAndSetItem().await()
|
||||
val discoveredItem = DiscoverItem(movie)
|
||||
val discoveredItem = seerrService.createDiscoverItem(movie)
|
||||
backdropService.submit(discoveredItem)
|
||||
|
||||
updateCanCancel()
|
||||
|
|
@ -121,7 +121,7 @@ class DiscoverMovieViewModel
|
|||
seerrService.api.moviesApi
|
||||
.movieMovieIdSimilarGet(movieId = item.id, page = 1)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { seerrService.createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
similar.setValueOnMain(result)
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ class DiscoverMovieViewModel
|
|||
seerrService.api.moviesApi
|
||||
.movieMovieIdRecommendationsGet(movieId = item.id, page = 1)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { seerrService.createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
recommended.setValueOnMain(result)
|
||||
}
|
||||
|
|
@ -138,11 +138,11 @@ class DiscoverMovieViewModel
|
|||
val people =
|
||||
movie.credits
|
||||
?.cast
|
||||
?.map(::DiscoverItem)
|
||||
?.map { seerrService.createDiscoverItem(it) }
|
||||
.orEmpty() +
|
||||
movie.credits
|
||||
?.crew
|
||||
?.map(::DiscoverItem)
|
||||
?.map { seerrService.createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
this@DiscoverMovieViewModel.people.setValueOnMain(people)
|
||||
val trailers =
|
||||
|
|
|
|||
|
|
@ -67,11 +67,11 @@ class DiscoverPersonViewModel
|
|||
.let { credits ->
|
||||
val cast =
|
||||
credits.cast
|
||||
?.map(::DiscoverItem)
|
||||
?.map { seerrService.createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
val crew =
|
||||
credits.crew
|
||||
?.map(::DiscoverItem)
|
||||
?.map { seerrService.createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
cast + crew
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ class DiscoverSeriesViewModel
|
|||
) {
|
||||
Timber.v("Init for tv %s", item.id)
|
||||
val tv = fetchAndSetItem().await()
|
||||
val discoveredItem = DiscoverItem(tv)
|
||||
val discoveredItem = seerrService.createDiscoverItem(tv)
|
||||
backdropService.submit(discoveredItem)
|
||||
|
||||
updateSeasonStatus()
|
||||
|
|
@ -121,7 +121,7 @@ class DiscoverSeriesViewModel
|
|||
seerrService.api.tvApi
|
||||
.tvTvIdSimilarGet(tvId = item.id, page = 1)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { seerrService.createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
similar.setValueOnMain(result)
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ class DiscoverSeriesViewModel
|
|||
seerrService.api.tvApi
|
||||
.tvTvIdRecommendationsGet(tvId = item.id, page = 1)
|
||||
.results
|
||||
?.map(::DiscoverItem)
|
||||
?.map { seerrService.createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
recommended.setValueOnMain(result)
|
||||
}
|
||||
|
|
@ -138,11 +138,11 @@ class DiscoverSeriesViewModel
|
|||
val people =
|
||||
tv.credits
|
||||
?.cast
|
||||
?.map(::DiscoverItem)
|
||||
?.map { seerrService.createDiscoverItem(it) }
|
||||
.orEmpty() +
|
||||
tv.credits
|
||||
?.crew
|
||||
?.map(::DiscoverItem)
|
||||
?.map { seerrService.createDiscoverItem(it) }
|
||||
.orEmpty()
|
||||
this@DiscoverSeriesViewModel.people.setValueOnMain(people)
|
||||
|
||||
|
|
|
|||
|
|
@ -125,20 +125,6 @@ fun SeriesOverview(
|
|||
var rowFocused by rememberInt()
|
||||
var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) }
|
||||
|
||||
LaunchedEffect(episodes) {
|
||||
episodes?.let { episodes ->
|
||||
if (episodes is EpisodeList.Success) {
|
||||
if (episodes.episodes.isNotEmpty()) {
|
||||
// TODO focus on first episode when changing seasons?
|
||||
// firstItemFocusRequester.requestFocus()
|
||||
episodes.episodes.getOrNull(position.episodeRowIndex)?.let {
|
||||
viewModel.refreshEpisode(it.id, position.episodeRowIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(position, episodes) {
|
||||
val focusedEpisode =
|
||||
(episodes as? EpisodeList.Success)
|
||||
|
|
|
|||
|
|
@ -239,7 +239,9 @@ class SeriesViewModel
|
|||
val tv =
|
||||
if (active) {
|
||||
try {
|
||||
seerrService.getTvSeries(item)?.let { DiscoverItem(it) }
|
||||
seerrService
|
||||
.getTvSeries(item)
|
||||
?.let { seerrService.createDiscoverItem(it) }
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex)
|
||||
null
|
||||
|
|
@ -387,12 +389,6 @@ class SeriesViewModel
|
|||
withContext(Dispatchers.Main) {
|
||||
this@SeriesViewModel.episodes.value = episodes
|
||||
}
|
||||
if (currentEpisodes == null || currentEpisodes.seasonId != seasonId) {
|
||||
(episodes as? EpisodeList.Success)
|
||||
?.let {
|
||||
it.episodes.getOrNull(it.initialEpisodeIndex)
|
||||
}?.let { lookupPeopleInEpisode(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,13 +85,13 @@ class SeerrRequestsViewModel
|
|||
seerrService.api.moviesApi
|
||||
.movieMovieIdGet(
|
||||
movieId = request.media.tmdbId,
|
||||
).let { DiscoverItem(it) }
|
||||
).let { seerrService.createDiscoverItem(it) }
|
||||
}
|
||||
|
||||
SeerrItemType.TV -> {
|
||||
seerrService.api.tvApi
|
||||
.tvTvIdGet(tvId = request.media.tmdbId)
|
||||
.let { DiscoverItem(it) }
|
||||
.let { seerrService.createDiscoverItem(it) }
|
||||
}
|
||||
|
||||
SeerrItemType.PERSON -> {
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ class SearchViewModel
|
|||
val results =
|
||||
seerrService
|
||||
.search(query)
|
||||
.map { DiscoverItem(it) }
|
||||
.map { seerrService.createDiscoverItem(it) }
|
||||
.filter { it.type == SeerrItemType.MOVIE || it.type == SeerrItemType.TV }
|
||||
seerrResults.setValueOnMain(SearchResult.SuccessSeerr(results))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,37 +127,50 @@ class SwitchSeerrViewModel
|
|||
}
|
||||
|
||||
fun createUrls(url: String): List<HttpUrl> {
|
||||
val urls = mutableListOf<String>()
|
||||
val urls = mutableListOf<HttpUrl>()
|
||||
if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||
urls.add(url)
|
||||
val httpUrl = url.toHttpUrl()
|
||||
urls.add(httpUrl)
|
||||
if (HttpUrl.defaultPort(httpUrl.scheme) == httpUrl.port) {
|
||||
urls.add("$url:5055")
|
||||
urls.add(httpUrl.newBuilder().port(5055).build())
|
||||
}
|
||||
} else {
|
||||
urls.add("http://$url")
|
||||
val httpUrl = "http://$url".toHttpUrl()
|
||||
urls.add(httpUrl)
|
||||
if (httpUrl.port == 80) {
|
||||
urls.add("https://$url")
|
||||
urls.add("http://$url:5055")
|
||||
urls.add("https://$url:5055")
|
||||
urls.add(httpUrl.newBuilder().scheme("https").build())
|
||||
urls.add(httpUrl.newBuilder().port(5055).build())
|
||||
urls.add(
|
||||
httpUrl
|
||||
.newBuilder()
|
||||
.scheme("https")
|
||||
.port(5055)
|
||||
.build(),
|
||||
)
|
||||
} else {
|
||||
urls.add("https://$url")
|
||||
urls.add(httpUrl.newBuilder().scheme("https").build())
|
||||
}
|
||||
}
|
||||
return urls.map { cleanUrl(it).toHttpUrl() }
|
||||
return urls
|
||||
}
|
||||
|
||||
private fun cleanUrl(url: String) =
|
||||
if (!url.endsWith("/api/v1")) {
|
||||
fun createSeerrApiUrl(url: String): String =
|
||||
if (url.isBlank()) {
|
||||
url
|
||||
} else if (url.endsWith("/api/v1") || url.endsWith("/api/v1/")) {
|
||||
url
|
||||
} else {
|
||||
url
|
||||
.toHttpUrl()
|
||||
.newBuilder()
|
||||
.apply {
|
||||
addPathSegment("api")
|
||||
addPathSegment("v1")
|
||||
}.build()
|
||||
.addPathSegment("api")
|
||||
.addPathSegment("v1")
|
||||
.build()
|
||||
.toString()
|
||||
} else {
|
||||
url
|
||||
}
|
||||
|
||||
fun migrateSeerrUrl(url: String): String {
|
||||
var url = url.removeSuffix("/api/v1/").removeSuffix("/api/v1")
|
||||
if (!url.endsWith("/")) url += "/"
|
||||
return url
|
||||
}
|
||||
|
|
|
|||
|
|
@ -395,8 +395,9 @@ fun SlideshowPage(
|
|||
ImageRequest
|
||||
.Builder(LocalContext.current)
|
||||
.data(imageState.url)
|
||||
.size(Size.ORIGINAL)
|
||||
.transitionFactory(CrossFadeFactory(750.milliseconds))
|
||||
.apply {
|
||||
if (isZoomed) size(Size.ORIGINAL)
|
||||
}.transitionFactory(CrossFadeFactory(750.milliseconds))
|
||||
.useExistingImageAsPlaceholder(true)
|
||||
.build(),
|
||||
contentDescription = null,
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ class ApiRequestPager<T>(
|
|||
position: Int,
|
||||
itemId: UUID,
|
||||
) {
|
||||
mutex.withLock {
|
||||
val item =
|
||||
api.userLibraryApi.getItem(itemId).content.let {
|
||||
BaseItem.from(
|
||||
|
|
@ -156,7 +157,6 @@ class ApiRequestPager<T>(
|
|||
}
|
||||
val pageNumber = position / pageSize
|
||||
val index = position - pageNumber * pageSize
|
||||
mutex.withLock {
|
||||
val page = cachedPages.getIfPresent(pageNumber)
|
||||
if (page != null && index in page.indices) {
|
||||
page[index] = item
|
||||
|
|
|
|||
|
|
@ -711,6 +711,8 @@ components:
|
|||
type: boolean
|
||||
series4kEnabled:
|
||||
type: boolean
|
||||
cacheImages:
|
||||
type: boolean
|
||||
MovieResult:
|
||||
type: object
|
||||
required:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.github.damontecres.wholphin.test
|
||||
|
||||
import com.github.damontecres.wholphin.ui.setup.seerr.createSeerrApiUrl
|
||||
import com.github.damontecres.wholphin.ui.setup.seerr.createUrls
|
||||
import org.junit.Assert
|
||||
import com.github.damontecres.wholphin.ui.setup.seerr.migrateSeerrUrl
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class TestSeerr {
|
||||
|
|
@ -13,12 +15,12 @@ class TestSeerr {
|
|||
|
||||
val expected =
|
||||
listOf(
|
||||
"http://jellyseerr.com/api/v1",
|
||||
"https://jellyseerr.com/api/v1",
|
||||
"http://jellyseerr.com:5055/api/v1",
|
||||
"https://jellyseerr.com:5055/api/v1",
|
||||
"http://jellyseerr.com/",
|
||||
"https://jellyseerr.com/",
|
||||
"http://jellyseerr.com:5055/",
|
||||
"https://jellyseerr.com:5055/",
|
||||
)
|
||||
Assert.assertEquals(expected, urls)
|
||||
assertEquals(expected, urls)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -29,10 +31,10 @@ class TestSeerr {
|
|||
|
||||
val expected =
|
||||
listOf(
|
||||
"https://jellyseerr.com/api/v1",
|
||||
"https://jellyseerr.com:5055/api/v1",
|
||||
"https://jellyseerr.com/",
|
||||
"https://jellyseerr.com:5055/",
|
||||
)
|
||||
Assert.assertEquals(expected, urls)
|
||||
assertEquals(expected, urls)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -43,10 +45,10 @@ class TestSeerr {
|
|||
|
||||
val expected =
|
||||
listOf(
|
||||
"http://jellyseerr.com/api/v1",
|
||||
"http://jellyseerr.com:5055/api/v1",
|
||||
"http://jellyseerr.com/",
|
||||
"http://jellyseerr.com:5055/",
|
||||
)
|
||||
Assert.assertEquals(expected, urls)
|
||||
assertEquals(expected, urls)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -57,10 +59,10 @@ class TestSeerr {
|
|||
|
||||
val expected =
|
||||
listOf(
|
||||
"http://jellyseerr.com:5055/api/v1",
|
||||
"https://jellyseerr.com:5055/api/v1",
|
||||
"http://jellyseerr.com:5055/",
|
||||
"https://jellyseerr.com:5055/",
|
||||
)
|
||||
Assert.assertEquals(expected, urls)
|
||||
assertEquals(expected, urls)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -71,10 +73,10 @@ class TestSeerr {
|
|||
|
||||
val expected =
|
||||
listOf(
|
||||
"http://10.0.0.2:443/api/v1",
|
||||
"https://10.0.0.2/api/v1",
|
||||
"http://10.0.0.2:443/",
|
||||
"https://10.0.0.2/",
|
||||
)
|
||||
Assert.assertEquals(expected, urls)
|
||||
assertEquals(expected, urls)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -85,9 +87,87 @@ class TestSeerr {
|
|||
|
||||
val expected =
|
||||
listOf(
|
||||
"http://10.0.0.2:8080/api/v1",
|
||||
"https://10.0.0.2:8080/api/v1",
|
||||
"http://10.0.0.2:8080/",
|
||||
"https://10.0.0.2:8080/",
|
||||
)
|
||||
Assert.assertEquals(expected, urls)
|
||||
assertEquals(expected, urls)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateUrls7() {
|
||||
val urls =
|
||||
createUrls("http://10.0.0.2:80")
|
||||
.map { it.toString() }
|
||||
|
||||
val expected =
|
||||
listOf(
|
||||
"http://10.0.0.2/",
|
||||
"http://10.0.0.2:5055/",
|
||||
)
|
||||
assertEquals(expected, urls)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateUrls8() {
|
||||
val urls =
|
||||
createUrls("https://10.0.0.2:443")
|
||||
.map { it.toString() }
|
||||
|
||||
val expected =
|
||||
listOf(
|
||||
"https://10.0.0.2/",
|
||||
"https://10.0.0.2:5055/",
|
||||
)
|
||||
assertEquals(expected, urls)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Test createUrls for path`() {
|
||||
val urls =
|
||||
createUrls("https://jellyseerr.com/seerr/")
|
||||
.map { it.toString() }
|
||||
|
||||
val expected =
|
||||
listOf(
|
||||
"https://jellyseerr.com/seerr/",
|
||||
"https://jellyseerr.com:5055/seerr/",
|
||||
)
|
||||
assertEquals(expected, urls)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Test build api url`() {
|
||||
var url = "https://jellyseerr.com/"
|
||||
assertEquals("https://jellyseerr.com/api/v1", createSeerrApiUrl(url))
|
||||
|
||||
url = "https://jellyseerr.com/path"
|
||||
assertEquals("https://jellyseerr.com/path/api/v1", createSeerrApiUrl(url))
|
||||
|
||||
url = "http://jellyseerr.com:5055/"
|
||||
assertEquals("http://jellyseerr.com:5055/api/v1", createSeerrApiUrl(url))
|
||||
|
||||
url = "http://jellyseerr.com:7878/path/"
|
||||
assertEquals("http://jellyseerr.com:7878/path/api/v1", createSeerrApiUrl(url))
|
||||
|
||||
url = "http://jellyseerr.com/api/v1"
|
||||
assertEquals("http://jellyseerr.com/api/v1", createSeerrApiUrl(url))
|
||||
|
||||
url = "http://jellyseerr.com/api/v1/"
|
||||
assertEquals("http://jellyseerr.com/api/v1/", createSeerrApiUrl(url))
|
||||
|
||||
url = "http://jellyseerr.com/path/api/v1"
|
||||
assertEquals("http://jellyseerr.com/path/api/v1", createSeerrApiUrl(url))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Test migration`() {
|
||||
assertEquals("http://10.0.0.2/", migrateSeerrUrl("http://10.0.0.2/api/v1"))
|
||||
assertEquals("https://10.0.0.2/", migrateSeerrUrl("https://10.0.0.2/api/v1"))
|
||||
assertEquals("http://10.0.0.2:5055/", migrateSeerrUrl("http://10.0.0.2:5055/api/v1"))
|
||||
assertEquals("http://10.0.0.2/", migrateSeerrUrl("http://10.0.0.2/api/v1/"))
|
||||
assertEquals("http://10.0.0.2/path/", migrateSeerrUrl("http://10.0.0.2/path/api/v1"))
|
||||
assertEquals("http://10.0.0.2/api/v1/", migrateSeerrUrl("http://10.0.0.2/api/v1/api/v1"))
|
||||
assertEquals("http://10.0.0.2/", migrateSeerrUrl("http://10.0.0.2/"))
|
||||
assertEquals("http://10.0.0.2/", migrateSeerrUrl("http://10.0.0.2"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
package com.github.damontecres.wholphin.test
|
||||
|
||||
import com.github.damontecres.wholphin.services.getDownloadUrl
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class TestUpdateChecker {
|
||||
lateinit var releaseJson: JsonObject
|
||||
val assetsJson: JsonArray by lazy { releaseJson["assets"]!!.jsonArray }
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
val resource = javaClass.classLoader?.getResource("release_develop.json")
|
||||
Assert.assertNotNull(resource)
|
||||
val fileContents = Paths.get(resource!!.toURI()).readText()
|
||||
releaseJson = Json.parseToJsonElement(fileContents).jsonObject
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Release chooses release`() {
|
||||
val url = getDownloadUrl(assetsJson, false, listOf())
|
||||
Assert.assertEquals("https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-release.apk", url)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Choose abi`() {
|
||||
val url = getDownloadUrl(assetsJson, false, listOf("arm64-v8a"))
|
||||
Assert.assertEquals("https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-release-arm64-v8a.apk", url)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Choose unknown abi`() {
|
||||
val url = getDownloadUrl(assetsJson, false, listOf("unknown"))
|
||||
Assert.assertEquals("https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-release.apk", url)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Debug chooses debug`() {
|
||||
val url = getDownloadUrl(assetsJson, true, listOf())
|
||||
Assert.assertEquals("https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-debug.apk", url)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Choose debug abi`() {
|
||||
val url = getDownloadUrl(assetsJson, true, listOf("arm64-v8a"))
|
||||
Assert.assertEquals("https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-debug-arm64-v8a.apk", url)
|
||||
}
|
||||
}
|
||||
475
app/src/test/resources/release_develop.json
Normal file
475
app/src/test/resources/release_develop.json
Normal file
|
|
@ -0,0 +1,475 @@
|
|||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/297064448",
|
||||
"assets_url": "https://api.github.com/repos/damontecres/Wholphin/releases/297064448/assets",
|
||||
"upload_url": "https://uploads.github.com/repos/damontecres/Wholphin/releases/297064448/assets{?name,label}",
|
||||
"html_url": "https://github.com/damontecres/Wholphin/releases/tag/develop",
|
||||
"id": 297064448,
|
||||
"author": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"node_id": "RE_kwDOPzwRps4RtNgA",
|
||||
"tag_name": "develop",
|
||||
"target_commitish": "main",
|
||||
"name": "v0.5.3-8-g627b89f1",
|
||||
"draft": false,
|
||||
"immutable": false,
|
||||
"prerelease": true,
|
||||
"created_at": "2026-03-14T20:47:08Z",
|
||||
"updated_at": "2026-03-14T20:54:31Z",
|
||||
"published_at": "2026-03-14T20:54:31Z",
|
||||
"assets": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937404",
|
||||
"id": 373937404,
|
||||
"node_id": "RA_kwDOPzwRps4WSdT8",
|
||||
"name": "Wholphin-debug-0.5.3-8-g627b89f1-44-arm64-v8a.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 65936692,
|
||||
"digest": "sha256:ede68f14374dea27a92063d1aab765265cf5386c93533b01e7c666c4b8a6ea99",
|
||||
"download_count": 0,
|
||||
"created_at": "2026-03-14T20:54:23Z",
|
||||
"updated_at": "2026-03-14T20:54:26Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-debug-0.5.3-8-g627b89f1-44-arm64-v8a.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937407",
|
||||
"id": 373937407,
|
||||
"node_id": "RA_kwDOPzwRps4WSdT_",
|
||||
"name": "Wholphin-debug-0.5.3-8-g627b89f1-44-armeabi-v7a.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 62726455,
|
||||
"digest": "sha256:c2c6d0443af1302a8aa8a70426605e6ca67345b98a74b5b1d22b85e7de9012c2",
|
||||
"download_count": 0,
|
||||
"created_at": "2026-03-14T20:54:24Z",
|
||||
"updated_at": "2026-03-14T20:54:25Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-debug-0.5.3-8-g627b89f1-44-armeabi-v7a.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937406",
|
||||
"id": 373937406,
|
||||
"node_id": "RA_kwDOPzwRps4WSdT-",
|
||||
"name": "Wholphin-debug-0.5.3-8-g627b89f1-44.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 99552740,
|
||||
"digest": "sha256:d0f5bbbe0c6f6bc31f681d70d60a5730dd0dbc83f28e66922fde27f4e60cda05",
|
||||
"download_count": 0,
|
||||
"created_at": "2026-03-14T20:54:24Z",
|
||||
"updated_at": "2026-03-14T20:54:26Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-debug-0.5.3-8-g627b89f1-44.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937405",
|
||||
"id": 373937405,
|
||||
"node_id": "RA_kwDOPzwRps4WSdT9",
|
||||
"name": "Wholphin-debug-arm64-v8a.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 65936692,
|
||||
"digest": "sha256:ede68f14374dea27a92063d1aab765265cf5386c93533b01e7c666c4b8a6ea99",
|
||||
"download_count": 0,
|
||||
"created_at": "2026-03-14T20:54:23Z",
|
||||
"updated_at": "2026-03-14T20:54:26Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-debug-arm64-v8a.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937408",
|
||||
"id": 373937408,
|
||||
"node_id": "RA_kwDOPzwRps4WSdUA",
|
||||
"name": "Wholphin-debug-armeabi-v7a.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 62726455,
|
||||
"digest": "sha256:c2c6d0443af1302a8aa8a70426605e6ca67345b98a74b5b1d22b85e7de9012c2",
|
||||
"download_count": 0,
|
||||
"created_at": "2026-03-14T20:54:24Z",
|
||||
"updated_at": "2026-03-14T20:54:26Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-debug-armeabi-v7a.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937422",
|
||||
"id": 373937422,
|
||||
"node_id": "RA_kwDOPzwRps4WSdUO",
|
||||
"name": "Wholphin-debug.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 99552740,
|
||||
"digest": "sha256:d0f5bbbe0c6f6bc31f681d70d60a5730dd0dbc83f28e66922fde27f4e60cda05",
|
||||
"download_count": 0,
|
||||
"created_at": "2026-03-14T20:54:26Z",
|
||||
"updated_at": "2026-03-14T20:54:29Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-debug.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937424",
|
||||
"id": 373937424,
|
||||
"node_id": "RA_kwDOPzwRps4WSdUQ",
|
||||
"name": "Wholphin-release-0.5.3-8-g627b89f1-44-arm64-v8a.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 56651710,
|
||||
"digest": "sha256:bf010482a8b8626fb93bbb3285667b22a41c3548286d3319bda92c71eedba672",
|
||||
"download_count": 0,
|
||||
"created_at": "2026-03-14T20:54:26Z",
|
||||
"updated_at": "2026-03-14T20:54:28Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-release-0.5.3-8-g627b89f1-44-arm64-v8a.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937427",
|
||||
"id": 373937427,
|
||||
"node_id": "RA_kwDOPzwRps4WSdUT",
|
||||
"name": "Wholphin-release-0.5.3-8-g627b89f1-44-armeabi-v7a.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 53441542,
|
||||
"digest": "sha256:5b8aee8f79fd5ae26dbd5de4a1b4d38b90bc6b35cd2e7fa5f2683909463da390",
|
||||
"download_count": 0,
|
||||
"created_at": "2026-03-14T20:54:26Z",
|
||||
"updated_at": "2026-03-14T20:54:28Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-release-0.5.3-8-g627b89f1-44-armeabi-v7a.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937431",
|
||||
"id": 373937431,
|
||||
"node_id": "RA_kwDOPzwRps4WSdUX",
|
||||
"name": "Wholphin-release-0.5.3-8-g627b89f1-44.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 90267831,
|
||||
"digest": "sha256:bc10956a072a5a07c372a67ee8732171470529b468fd7d85a277a738604c34c5",
|
||||
"download_count": 0,
|
||||
"created_at": "2026-03-14T20:54:27Z",
|
||||
"updated_at": "2026-03-14T20:54:29Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-release-0.5.3-8-g627b89f1-44.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937439",
|
||||
"id": 373937439,
|
||||
"node_id": "RA_kwDOPzwRps4WSdUf",
|
||||
"name": "Wholphin-release-arm64-v8a.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 56651710,
|
||||
"digest": "sha256:bf010482a8b8626fb93bbb3285667b22a41c3548286d3319bda92c71eedba672",
|
||||
"download_count": 1,
|
||||
"created_at": "2026-03-14T20:54:27Z",
|
||||
"updated_at": "2026-03-14T20:54:29Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-release-arm64-v8a.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937452",
|
||||
"id": 373937452,
|
||||
"node_id": "RA_kwDOPzwRps4WSdUs",
|
||||
"name": "Wholphin-release-armeabi-v7a.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 53441542,
|
||||
"digest": "sha256:5b8aee8f79fd5ae26dbd5de4a1b4d38b90bc6b35cd2e7fa5f2683909463da390",
|
||||
"download_count": 0,
|
||||
"created_at": "2026-03-14T20:54:28Z",
|
||||
"updated_at": "2026-03-14T20:54:30Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-release-armeabi-v7a.apk"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/damontecres/Wholphin/releases/assets/373937453",
|
||||
"id": 373937453,
|
||||
"node_id": "RA_kwDOPzwRps4WSdUt",
|
||||
"name": "Wholphin-release.apk",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/vnd.android.package-archive",
|
||||
"state": "uploaded",
|
||||
"size": 90267831,
|
||||
"digest": "sha256:bc10956a072a5a07c372a67ee8732171470529b468fd7d85a277a738604c34c5",
|
||||
"download_count": 1,
|
||||
"created_at": "2026-03-14T20:54:28Z",
|
||||
"updated_at": "2026-03-14T20:54:31Z",
|
||||
"browser_download_url": "https://github.com/damontecres/Wholphin/releases/download/develop/Wholphin-release.apk"
|
||||
}
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/damontecres/Wholphin/tarball/develop",
|
||||
"zipball_url": "https://api.github.com/repos/damontecres/Wholphin/zipball/develop",
|
||||
"body": "### `main` development build\n\nThis pre-release tracks the development build of Wholphin from the `main` unstable branch.\n\nSee https://github.com/damontecres/Wholphin/releases/latest for the latest stable release.\n\nIf you want to update to this version in-app, change Settings->Advanced->Update URL to https://api.github.com/repos/damontecres/Wholphin/releases/tags/develop (replace `latest` with `tags/develop`).\n\n[See changes since `v0.5.3`](https://github.com/damontecres/Wholphin/compare/v0.5.3...develop)\n"
|
||||
}
|
||||
|
|
@ -30,7 +30,11 @@ clone "https://gitlab.freedesktop.org/freetype/freetype.git" "VER-2-14-1" freety
|
|||
|
||||
clone "https://github.com/libass/libass" "0.17.4" libass
|
||||
|
||||
clone "https://github.com/haasn/libplacebo" "v7.351.0" libplacebo --recurse-submodules
|
||||
rm -rf libplacebo
|
||||
git clone "https://github.com/haasn/libplacebo" --single-branch -b "master" --recurse-submodules libplacebo
|
||||
pushd libplacebo || exit
|
||||
git checkout 1bd8d2d6a715bc870bdffa6759e62c419000dd51
|
||||
popd || exit
|
||||
|
||||
clone "https://github.com/mpv-player/mpv" "v0.41.0" mpv
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue