Several minor bug fixes (#1811)
## Description Fixes several small bugs and does some minor refactoring: - Use "Recently Added in XYZ" instead of "Recently recorded" for recorded tv libraries - Improve home page query performance by using fewer fields in the query - When an error occurs connecting to discovered servers, show the error instead of only logging - Fix headers sometimes appearing briefly on some pages when navigating back - Catch exceptions in socket connections - Use a larger buffer when downloading updates - Split up ADB logging for media info ### Related issues Related to https://github.com/damontecres/Wholphin/issues/1517#issuecomment-5184316086 Maybe the larger download buffer will help with https://github.com/damontecres/Wholphin/issues/1696 Will help show the error in #1801 Prevents the crash in #1786 ### Testing Emulator & nvidia shield ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
54966f9124
commit
51ab39ed3b
17 changed files with 282 additions and 250 deletions
|
|
@ -12,9 +12,8 @@ import com.github.damontecres.wholphin.data.model.createGenreDestination
|
|||
import com.github.damontecres.wholphin.data.model.createStudioDestination
|
||||
import com.github.damontecres.wholphin.preferences.DefaultUserConfiguration
|
||||
import com.github.damontecres.wholphin.preferences.HomePagePreferences
|
||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
||||
import com.github.damontecres.wholphin.ui.HomeItemFields
|
||||
import com.github.damontecres.wholphin.ui.ProgramItemFields
|
||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||
import com.github.damontecres.wholphin.ui.components.getGenreImageMap
|
||||
import com.github.damontecres.wholphin.ui.main.settings.Library
|
||||
import com.github.damontecres.wholphin.ui.main.settings.favoriteOptions
|
||||
|
|
@ -62,6 +61,7 @@ import org.jellyfin.sdk.model.api.BaseItemKind
|
|||
import org.jellyfin.sdk.model.api.CollectionType
|
||||
import org.jellyfin.sdk.model.api.GetProgramsDto
|
||||
import org.jellyfin.sdk.model.api.ImageType
|
||||
import org.jellyfin.sdk.model.api.ItemFields
|
||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||
import org.jellyfin.sdk.model.api.SortOrder
|
||||
import org.jellyfin.sdk.model.api.UserDto
|
||||
|
|
@ -280,7 +280,7 @@ class HomeSettingsService
|
|||
libraries
|
||||
.mapIndexed { index, it ->
|
||||
val parentId = it.itemId
|
||||
val title = getRecentlyAddedTitle(it)
|
||||
val title = getRecentlyAddedTitle(it.name)
|
||||
if (it.collectionType == CollectionType.LIVETV) {
|
||||
HomeRowConfigDisplay(
|
||||
id = index,
|
||||
|
|
@ -766,13 +766,11 @@ class HomeSettingsService
|
|||
}
|
||||
|
||||
is HomeRowConfig.RecentlyAdded -> {
|
||||
val library =
|
||||
libraries
|
||||
.firstOrNull { it.itemId == row.parentId }
|
||||
val title = getRecentlyAddedTitle(library)
|
||||
val library = libraries.firstOrNull { it.itemId == row.parentId }
|
||||
val title = getRecentlyAddedTitle(library?.name)
|
||||
val request =
|
||||
GetLatestMediaRequest(
|
||||
fields = SlimItemFields,
|
||||
fields = library.itemFields,
|
||||
imageTypeLimit = 1,
|
||||
parentId = row.parentId,
|
||||
groupItems = true,
|
||||
|
|
@ -797,12 +795,9 @@ class HomeSettingsService
|
|||
}
|
||||
|
||||
is HomeRowConfig.RecentlyReleased -> {
|
||||
val name =
|
||||
libraries
|
||||
.firstOrNull { it.itemId == row.parentId }
|
||||
?.name
|
||||
val library = libraries.firstOrNull { it.itemId == row.parentId }
|
||||
val title =
|
||||
name?.let {
|
||||
library?.name?.let {
|
||||
ResArgStringProvider(R.string.recently_released_in, it)
|
||||
} ?: ResStringProvider(R.string.recently_released)
|
||||
val request =
|
||||
|
|
@ -821,7 +816,7 @@ class HomeSettingsService
|
|||
SortOrder.ASCENDING,
|
||||
SortOrder.DESCENDING,
|
||||
),
|
||||
fields = DefaultItemFields,
|
||||
fields = library.itemFields,
|
||||
recursive = true,
|
||||
maxPremiereDate = LocalDateTime.now(),
|
||||
isUnaired = false,
|
||||
|
|
@ -851,6 +846,7 @@ class HomeSettingsService
|
|||
}
|
||||
|
||||
is HomeRowConfig.ByParent -> {
|
||||
val library = libraries.firstOrNull { it.itemId == row.parentId }
|
||||
val request =
|
||||
GetItemsRequest(
|
||||
userId = userDto.id,
|
||||
|
|
@ -885,7 +881,7 @@ class HomeSettingsService
|
|||
}
|
||||
},
|
||||
limit = limit,
|
||||
fields = DefaultItemFields,
|
||||
fields = library.itemFields,
|
||||
)
|
||||
|
||||
// Not using getItemName because we want to throw the 404
|
||||
|
|
@ -970,7 +966,7 @@ class HomeSettingsService
|
|||
GetPersonsRequest(
|
||||
userId = userDto.id,
|
||||
limit = limit,
|
||||
fields = DefaultItemFields,
|
||||
fields = HomeItemFields,
|
||||
isFavorite = true,
|
||||
enableImages = true,
|
||||
enableImageTypes = listOf(ImageType.PRIMARY),
|
||||
|
|
@ -988,12 +984,18 @@ class HomeSettingsService
|
|||
)
|
||||
}
|
||||
} else {
|
||||
val fields =
|
||||
if (row.kind == BaseItemKind.BOX_SET) {
|
||||
HomeItemFieldsBoxSets
|
||||
} else {
|
||||
HomeItemFields
|
||||
}
|
||||
val request =
|
||||
GetItemsRequest(
|
||||
userId = userDto.id,
|
||||
recursive = true,
|
||||
limit = limit,
|
||||
fields = DefaultItemFields,
|
||||
fields = fields,
|
||||
includeItemTypes = listOf(row.kind),
|
||||
isFavorite = true,
|
||||
)
|
||||
|
|
@ -1027,7 +1029,7 @@ class HomeSettingsService
|
|||
GetRecordingsRequest(
|
||||
userId = userDto.id,
|
||||
isInProgress = true,
|
||||
fields = DefaultItemFields,
|
||||
fields = HomeItemFields,
|
||||
limit = limit,
|
||||
enableImages = true,
|
||||
enableUserData = true,
|
||||
|
|
@ -1098,7 +1100,7 @@ class HomeSettingsService
|
|||
val request =
|
||||
GetLiveTvChannelsRequest(
|
||||
userId = userDto.id,
|
||||
fields = DefaultItemFields,
|
||||
fields = HomeItemFields,
|
||||
limit = limit,
|
||||
enableImages = true,
|
||||
)
|
||||
|
|
@ -1225,10 +1227,17 @@ class UnsupportedHomeSettingsVersionException(
|
|||
val maxSupportedVersion: Int = SUPPORTED_HOME_PAGE_SETTINGS_VERSION,
|
||||
) : Exception("Unsupported version $unsupportedVersion, max supported is $maxSupportedVersion")
|
||||
|
||||
fun getRecentlyAddedTitle(library: Library?): StringProvider =
|
||||
if (library?.isRecordingFolder == true) {
|
||||
ResStringProvider(R.string.recently_recorded)
|
||||
} else {
|
||||
library?.name?.let { ResArgStringProvider(R.string.recently_added_in, it) }
|
||||
?: ResStringProvider(R.string.recently_added)
|
||||
}
|
||||
fun getRecentlyAddedTitle(name: String?): StringProvider =
|
||||
name?.let { ResArgStringProvider(R.string.recently_added_in, it) }
|
||||
?: ResStringProvider(R.string.recently_added)
|
||||
|
||||
private val Library?.itemFields: List<ItemFields>
|
||||
get() =
|
||||
if (this?.type == BaseItemKind.COLLECTION_FOLDER && this.collectionType == CollectionType.BOXSETS) {
|
||||
// Get child count to show in the header
|
||||
HomeItemFieldsBoxSets
|
||||
} else {
|
||||
HomeItemFields
|
||||
}
|
||||
|
||||
private val HomeItemFieldsBoxSets get() = HomeItemFields + listOf(ItemFields.CHILD_COUNT)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.github.damontecres.wholphin.services.hilt.IoCoroutineScope
|
|||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.showToast
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import com.google.protobuf.MessageLiteOrBuilder
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.serialization.json.Json
|
||||
|
|
@ -76,21 +77,18 @@ class MediaReportService
|
|||
model=${Build.MODEL}
|
||||
apiLevel=${Build.VERSION.SDK_INT}
|
||||
|
||||
playbackPrefs=${
|
||||
appPreferences.playbackPreferences.toString().replace("\n", ", ")
|
||||
.replace("\t", " ")
|
||||
}
|
||||
experimental=${
|
||||
appPreferences.experimentalPreferences.toString().replace("\n", ", ")
|
||||
.replace("\t", " ")
|
||||
}
|
||||
|
||||
deviceProfile=$deviceProfileJson
|
||||
playbackPrefs=${appPreferences.playbackPreferences.toStringOneLine()}
|
||||
experimental=${appPreferences.experimentalPreferences.toStringOneLine()}
|
||||
|
||||
mediaSources=$sourcesJson
|
||||
|
||||
deviceProfile=$deviceProfileJson
|
||||
""".trimIndent()
|
||||
Timber.w(body)
|
||||
body.chunked(2048).forEach { Timber.w(it) }
|
||||
Timber.w("End send media info")
|
||||
val response by api.clientLogApi.logFile(body)
|
||||
showToast(context, "Sent! Filename=${response.fileName}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun MessageLiteOrBuilder.toStringOneLine(): String = toString().replace("\n", ", ").replace("\t", " ")
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.github.damontecres.wholphin.ui.showToast
|
|||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
|
|
@ -94,6 +95,8 @@ class ServerEventListener
|
|||
.joinToString("\n")
|
||||
showToast(context, toast, Toast.LENGTH_LONG)
|
||||
}
|
||||
}.catch { ex ->
|
||||
Timber.e(ex, "Error in websocket subscription")
|
||||
}.launchIn(activity.lifecycleScope)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.github.damontecres.wholphin.services
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
|
@ -61,8 +62,6 @@ class UpdateChecker
|
|||
|
||||
private const val APK_MIME_TYPE = "application/vnd.android.package-archive"
|
||||
|
||||
private const val PERMISSION_REQUEST_CODE = 123456
|
||||
|
||||
private val NOTE_REGEX = Regex("<!-- app-note:(.+) -->")
|
||||
|
||||
val ACTIVE = BuildConfig.UPDATING_ENABLED
|
||||
|
|
@ -158,8 +157,8 @@ class UpdateChecker
|
|||
|
||||
private fun getRelease(request: Request): Release? {
|
||||
return okHttpClient.newCall(request).execute().use {
|
||||
if (it.isSuccessful && it.body != null) {
|
||||
val result = Json.parseToJsonElement(it.body!!.string())
|
||||
if (it.isSuccessful) {
|
||||
val result = Json.parseToJsonElement(it.body.string())
|
||||
val name = result.jsonObject["name"]?.jsonPrimitive?.contentOrNull
|
||||
val version = Version.tryFromString(name)
|
||||
val publishedAt =
|
||||
|
|
@ -195,6 +194,7 @@ class UpdateChecker
|
|||
/**
|
||||
* Download and install an update
|
||||
*/
|
||||
@SuppressLint("RequestInstallPackagesPolicy")
|
||||
suspend fun installRelease(
|
||||
release: Release,
|
||||
callback: DownloadCallback,
|
||||
|
|
@ -208,7 +208,7 @@ class UpdateChecker
|
|||
.get()
|
||||
.build()
|
||||
okHttpClient.newCall(request).execute().use {
|
||||
if (it.isSuccessful && it.body != null) {
|
||||
if (it.isSuccessful) {
|
||||
Timber.v("Request successful for ${release.downloadUrl}")
|
||||
withContext(WholphinDispatchers.Main) {
|
||||
callback.contentLength(it.body.contentLength())
|
||||
|
|
@ -230,7 +230,7 @@ class UpdateChecker
|
|||
contentValues,
|
||||
)
|
||||
if (uri != null) {
|
||||
it.body!!.byteStream().use { input ->
|
||||
it.body.byteStream().use { input ->
|
||||
resolver.openOutputStream(uri).use { output ->
|
||||
copyTo(input, output!!, callback = callback)
|
||||
}
|
||||
|
|
@ -290,7 +290,7 @@ class UpdateChecker
|
|||
downloadDir.mkdirs()
|
||||
val targetFile = File(downloadDir, APK_NAME)
|
||||
targetFile.outputStream().use { output ->
|
||||
response.body!!.byteStream().use { input ->
|
||||
response.body.byteStream().use { input ->
|
||||
copyTo(input, output, callback = callback)
|
||||
}
|
||||
}
|
||||
|
|
@ -374,7 +374,7 @@ data class Release(
|
|||
Regex("https://github.com/\\w*/\\w+/pull/(\\d+)"),
|
||||
"#$1",
|
||||
)
|
||||
// Remove the last line for full changelog since its just a link
|
||||
// Remove the last line for full changelog since it's just a link
|
||||
.replace(Regex("\\*\\*Full Changelog\\*\\*.*"), "")
|
||||
}
|
||||
|
||||
|
|
@ -387,29 +387,30 @@ interface DownloadCallback {
|
|||
suspend fun copyTo(
|
||||
input: InputStream,
|
||||
out: OutputStream,
|
||||
bufferSize: Int = 16 * 1024,
|
||||
bufferSize: Int = 64 * 1024,
|
||||
callback: DownloadCallback,
|
||||
): Long {
|
||||
var bytesCopied: Long = 0
|
||||
val buffer = ByteArray(bufferSize)
|
||||
var bytes = input.read(buffer)
|
||||
while (bytes >= 0) {
|
||||
out.write(buffer, 0, bytes)
|
||||
bytesCopied += bytes
|
||||
withContext(WholphinDispatchers.Main) {
|
||||
callback.bytesDownloaded(bytesCopied)
|
||||
): Long =
|
||||
withContext(WholphinDispatchers.IO) {
|
||||
var bytesCopied: Long = 0
|
||||
val buffer = ByteArray(bufferSize)
|
||||
var bytes = input.read(buffer)
|
||||
while (bytes >= 0) {
|
||||
out.write(buffer, 0, bytes)
|
||||
bytesCopied += bytes
|
||||
withContext(WholphinDispatchers.Main) {
|
||||
callback.bytesDownloaded(bytesCopied)
|
||||
}
|
||||
bytes = input.read(buffer)
|
||||
}
|
||||
bytes = input.read(buffer)
|
||||
return@withContext bytesCopied
|
||||
}
|
||||
return bytesCopied
|
||||
}
|
||||
|
||||
fun getDownloadUrl(
|
||||
assets: JsonArray,
|
||||
debug: Boolean,
|
||||
supportedAbis: List<String> = Build.SUPPORTED_ABIS.toList(),
|
||||
supportedABIs: List<String> = Build.SUPPORTED_ABIS.toList(),
|
||||
): String? {
|
||||
val abiSuffix = supportedAbis.firstOrNull().let { if (it != null) "-$it" else "" }
|
||||
val abiSuffix = supportedABIs.firstOrNull().let { if (it != null) "-$it" else "" }
|
||||
val releaseSuffix = if (debug) "-debug" else "-release"
|
||||
val preferredNames =
|
||||
buildList {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue