refactor(logging): standardize logRequest function naming and remove redundant logging

This commit is contained in:
2026-05-14 18:29:58 +02:00
parent 1fc2c8cf05
commit e09b63f47e

View File

@@ -3,9 +3,9 @@ package hu.bbara.purefin.data.jellyfin.client
import android.content.Context import android.content.Context
import android.os.SystemClock import android.os.SystemClock
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import hu.bbara.purefin.core.data.JellyfinServerCandidate
import hu.bbara.purefin.core.data.PlaybackMethod import hu.bbara.purefin.core.data.PlaybackMethod
import hu.bbara.purefin.core.data.PlaybackReportContext import hu.bbara.purefin.core.data.PlaybackReportContext
import hu.bbara.purefin.core.data.JellyfinServerCandidate
import hu.bbara.purefin.core.data.QuickConnectSession import hu.bbara.purefin.core.data.QuickConnectSession
import hu.bbara.purefin.core.data.UserSessionRepository import hu.bbara.purefin.core.data.UserSessionRepository
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@@ -16,8 +16,8 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.api.client.Response import org.jellyfin.sdk.api.client.Response
import org.jellyfin.sdk.api.client.extensions.authenticateWithQuickConnect
import org.jellyfin.sdk.api.client.extensions.authenticateUserByName import org.jellyfin.sdk.api.client.extensions.authenticateUserByName
import org.jellyfin.sdk.api.client.extensions.authenticateWithQuickConnect
import org.jellyfin.sdk.api.client.extensions.clientLogApi import org.jellyfin.sdk.api.client.extensions.clientLogApi
import org.jellyfin.sdk.api.client.extensions.genresApi import org.jellyfin.sdk.api.client.extensions.genresApi
import org.jellyfin.sdk.api.client.extensions.itemsApi import org.jellyfin.sdk.api.client.extensions.itemsApi
@@ -195,13 +195,12 @@ class JellyfinApiClient @Inject constructor(
searchTerm = searchTerm, searchTerm = searchTerm,
recursive = true recursive = true
) )
Timber.tag("searchBySearchTerm").d(response.content.toString())
response.content.items response.content.items
} }
} }
suspend fun searchByGenre(genres: Set<String>): List<BaseItemDto> = withContext(Dispatchers.IO) { suspend fun searchByGenre(genres: Set<String>): List<BaseItemDto> = withContext(Dispatchers.IO) {
logRequest("searchMovie") { logRequest("searchByGenre") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest emptyList() return@logRequest emptyList()
} }
@@ -211,7 +210,6 @@ class JellyfinApiClient @Inject constructor(
genres = genres, genres = genres,
recursive = true recursive = true
) )
Timber.tag("searchByGenre").d(response.content.toString())
response.content.items response.content.items
} }
} }
@@ -226,13 +224,12 @@ class JellyfinApiClient @Inject constructor(
presetViews = listOf(CollectionType.MOVIES, CollectionType.TVSHOWS), presetViews = listOf(CollectionType.MOVIES, CollectionType.TVSHOWS),
includeHidden = false, includeHidden = false,
) )
Timber.tag("getLibraries").d(response.content.toString())
response.content.items response.content.items
} }
} }
suspend fun getLibraryContent(libraryId: UUID): List<BaseItemDto> = withContext(Dispatchers.IO) { suspend fun getLibraryContent(libraryId: UUID): List<BaseItemDto> = withContext(Dispatchers.IO) {
logRequest("getLibraryContent($libraryId)") { logRequest("getLibraryContent") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest emptyList() return@logRequest emptyList()
} }
@@ -246,7 +243,6 @@ class JellyfinApiClient @Inject constructor(
recursive = true, recursive = true,
) )
val response = api.itemsApi.getItems(getItemsRequest) val response = api.itemsApi.getItems(getItemsRequest)
Timber.tag("getLibraryContent").d(response.content.toString())
response.content.items response.content.items
} }
} }
@@ -264,7 +260,6 @@ class JellyfinApiClient @Inject constructor(
limit = 8, limit = 8,
enableTotalRecordCount = true, enableTotalRecordCount = true,
) )
Timber.tag("getSuggestions").d(response.content.toString())
response.content.items response.content.items
} }
} }
@@ -283,7 +278,6 @@ class JellyfinApiClient @Inject constructor(
startIndex = 0, startIndex = 0,
) )
val response: Response<BaseItemDtoQueryResult> = api.itemsApi.getResumeItems(getResumeItemsRequest) val response: Response<BaseItemDtoQueryResult> = api.itemsApi.getResumeItems(getResumeItemsRequest)
Timber.tag("getContinueWatching").d(response.content.toString())
response.content.items response.content.items
} }
} }
@@ -299,13 +293,12 @@ class JellyfinApiClient @Inject constructor(
enableResumable = false, enableResumable = false,
) )
val result = api.tvShowsApi.getNextUp(getNextUpRequest) val result = api.tvShowsApi.getNextUp(getNextUpRequest)
Timber.tag("getNextUpEpisodes").d(result.content.toString())
result.content.items result.content.items
} }
} }
suspend fun getLatestFromLibrary(libraryId: UUID): List<BaseItemDto> = withContext(Dispatchers.IO) { suspend fun getLatestFromLibrary(libraryId: UUID): List<BaseItemDto> = withContext(Dispatchers.IO) {
logRequest("getLatestFromLibrary($libraryId)") { logRequest("getLatestFromLibrary") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest emptyList() return@logRequest emptyList()
} }
@@ -316,24 +309,22 @@ class JellyfinApiClient @Inject constructor(
includeItemTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.EPISODE, BaseItemKind.SEASON), includeItemTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.EPISODE, BaseItemKind.SEASON),
limit = 10, limit = 10,
) )
Timber.tag("getLatestFromLibrary").d(response.content.toString())
response.content response.content
} }
} }
suspend fun getItemInfo(mediaId: UUID): BaseItemDto? = withContext(Dispatchers.IO) { suspend fun getItemInfo(mediaId: UUID): BaseItemDto? = withContext(Dispatchers.IO) {
logRequest("getItemInfo($mediaId)") { logRequest("getItemInfo") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest null return@logRequest null
} }
val result = api.userLibraryApi.getItem(itemId = mediaId, userId = getUserId()) val result = api.userLibraryApi.getItem(itemId = mediaId, userId = getUserId())
Timber.tag("getItemInfo").d(result.content.toString())
result.content result.content
} }
} }
suspend fun getSeasons(seriesId: UUID): List<BaseItemDto> = withContext(Dispatchers.IO) { suspend fun getSeasons(seriesId: UUID): List<BaseItemDto> = withContext(Dispatchers.IO) {
logRequest("getSeasons($seriesId)") { logRequest("getSeasons") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest emptyList() return@logRequest emptyList()
} }
@@ -343,13 +334,12 @@ class JellyfinApiClient @Inject constructor(
fields = defaultItemFields, fields = defaultItemFields,
enableUserData = true, enableUserData = true,
) )
Timber.tag("getSeasons").d(result.content.toString())
result.content.items result.content.items
} }
} }
suspend fun getEpisodesInSeason(seriesId: UUID, seasonId: UUID): List<BaseItemDto> = withContext(Dispatchers.IO) { suspend fun getEpisodesInSeason(seriesId: UUID, seasonId: UUID): List<BaseItemDto> = withContext(Dispatchers.IO) {
logRequest("getEpisodesInSeason(series=$seriesId, season=$seasonId)") { logRequest("getEpisodesInSeason") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest emptyList() return@logRequest emptyList()
} }
@@ -360,13 +350,12 @@ class JellyfinApiClient @Inject constructor(
fields = defaultItemFields + ItemFields.OVERVIEW, fields = defaultItemFields + ItemFields.OVERVIEW,
enableUserData = true, enableUserData = true,
) )
Timber.tag("getEpisodesInSeason").d(result.content.toString())
result.content.items result.content.items
} }
} }
suspend fun getNextEpisodes(episodeId: UUID, count: Int): List<BaseItemDto> = withContext(Dispatchers.IO) { suspend fun getNextEpisodes(episodeId: UUID, count: Int): List<BaseItemDto> = withContext(Dispatchers.IO) {
logRequest("getNextEpisodes($episodeId, count=$count)") { logRequest("getNextEpisodes") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest emptyList() return@logRequest emptyList()
} }
@@ -380,13 +369,12 @@ class JellyfinApiClient @Inject constructor(
limit = count, limit = count,
) )
val nextUpEpisodes = nextUpEpisodesResult.content.items val nextUpEpisodes = nextUpEpisodesResult.content.items
Timber.tag("getNextEpisodes").d(nextUpEpisodes.toString())
nextUpEpisodes nextUpEpisodes
} }
} }
suspend fun getGenres(id: UUID? = null) : List<BaseItemDto> = withContext(Dispatchers.IO) { suspend fun getGenres(id: UUID? = null) : List<BaseItemDto> = withContext(Dispatchers.IO) {
logRequest("getGenres($id)") { logRequest("getGenres") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest emptyList() return@logRequest emptyList()
} }
@@ -394,13 +382,12 @@ class JellyfinApiClient @Inject constructor(
userId = getUserId(), userId = getUserId(),
parentId = id, parentId = id,
) )
Timber.tag("getGenres").d(result.toString())
result.content.items result.content.items
} }
} }
suspend fun markAsWatched(mediaId: UUID) = withContext(Dispatchers.IO) { suspend fun markAsWatched(mediaId: UUID) = withContext(Dispatchers.IO) {
logRequest("markAsWatched($mediaId)") { logRequest("markAsWatched") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest return@logRequest
} }
@@ -412,7 +399,7 @@ class JellyfinApiClient @Inject constructor(
} }
suspend fun markAsUnwatched(mediaId: UUID) = withContext(Dispatchers.IO) { suspend fun markAsUnwatched(mediaId: UUID) = withContext(Dispatchers.IO) {
logRequest("markAsUnwatched($mediaId)") { logRequest("markAsUnwatched") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest return@logRequest
} }
@@ -424,7 +411,7 @@ class JellyfinApiClient @Inject constructor(
} }
suspend fun getMediaSources(mediaId: UUID): List<MediaSourceInfo> = withContext(Dispatchers.IO) { suspend fun getMediaSources(mediaId: UUID): List<MediaSourceInfo> = withContext(Dispatchers.IO) {
logRequest("getMediaSources($mediaId)") { logRequest("getMediaSources") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest emptyList() return@logRequest emptyList()
} }
@@ -436,13 +423,12 @@ class JellyfinApiClient @Inject constructor(
maxStreamingBitrate = 100_000_000, maxStreamingBitrate = 100_000_000,
), ),
) )
Timber.tag("getMediaSources").d(result.toString())
result.content.mediaSources result.content.mediaSources
} }
} }
suspend fun getMediaSegments(mediaId: UUID) : List<MediaSegmentDto> = withContext(Dispatchers.IO) { suspend fun getMediaSegments(mediaId: UUID) : List<MediaSegmentDto> = withContext(Dispatchers.IO) {
logRequest("getMediaSegments($mediaId)") { logRequest("getMediaSegments") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest emptyList() return@logRequest emptyList()
} }
@@ -450,7 +436,6 @@ class JellyfinApiClient @Inject constructor(
itemId = mediaId, itemId = mediaId,
//includeSegmentTypes = listOf(MediaSegmentType.INTRO) //includeSegmentTypes = listOf(MediaSegmentType.INTRO)
) )
Timber.tag("getMediaSegments").d(result.toString())
result.content.items result.content.items
} }
} }
@@ -459,7 +444,7 @@ class JellyfinApiClient @Inject constructor(
mediaId: UUID, mediaId: UUID,
deviceProfile: DeviceProfile, deviceProfile: DeviceProfile,
): PlaybackInfoResponse? = withContext(Dispatchers.IO) { ): PlaybackInfoResponse? = withContext(Dispatchers.IO) {
logRequest("getPlaybackInfo($mediaId)") { logRequest("getPlaybackInfo") {
if (!ensureConfigured()) { if (!ensureConfigured()) {
return@logRequest null return@logRequest null
} }
@@ -497,7 +482,7 @@ class JellyfinApiClient @Inject constructor(
liveStreamId = liveStreamId, liveStreamId = liveStreamId,
) )
} catch (error: Exception) { } catch (error: Exception) {
Timber.tag(TAG).e(error, "getVideoStreamUrl($itemId) failed") Timber.tag(TAG).e(error, "getVideoStreamUrl")
throw error throw error
} }
@@ -524,7 +509,7 @@ class JellyfinApiClient @Inject constructor(
positionTicks: Long = 0L, positionTicks: Long = 0L,
reportContext: PlaybackReportContext, reportContext: PlaybackReportContext,
) = withContext(Dispatchers.IO) { ) = withContext(Dispatchers.IO) {
logRequest("reportPlaybackStart($itemId)") { logRequest("reportPlaybackStart") {
if (!ensureConfigured()) return@logRequest if (!ensureConfigured()) return@logRequest
api.playStateApi.reportPlaybackStart( api.playStateApi.reportPlaybackStart(
PlaybackStartInfo( PlaybackStartInfo(
@@ -552,7 +537,7 @@ class JellyfinApiClient @Inject constructor(
isPaused: Boolean, isPaused: Boolean,
reportContext: PlaybackReportContext, reportContext: PlaybackReportContext,
) = withContext(Dispatchers.IO) { ) = withContext(Dispatchers.IO) {
logRequest("reportPlaybackProgress($itemId)") { logRequest("reportPlaybackProgress") {
if (!ensureConfigured()) return@logRequest if (!ensureConfigured()) return@logRequest
api.playStateApi.reportPlaybackProgress( api.playStateApi.reportPlaybackProgress(
PlaybackProgressInfo( PlaybackProgressInfo(
@@ -579,7 +564,7 @@ class JellyfinApiClient @Inject constructor(
positionTicks: Long, positionTicks: Long,
reportContext: PlaybackReportContext, reportContext: PlaybackReportContext,
) = withContext(Dispatchers.IO) { ) = withContext(Dispatchers.IO) {
logRequest("reportPlaybackStopped($itemId)") { logRequest("reportPlaybackStopped") {
if (!ensureConfigured()) return@logRequest if (!ensureConfigured()) return@logRequest
api.playStateApi.reportPlaybackStopped( api.playStateApi.reportPlaybackStopped(
PlaybackStopInfo( PlaybackStopInfo(
@@ -594,39 +579,22 @@ class JellyfinApiClient @Inject constructor(
} }
} }
private suspend fun <T> logRequest(operation: String, block: suspend () -> T): T { private suspend fun <T> logRequest(functionName: String, block: suspend () -> T): T {
val startedAt = SystemClock.elapsedRealtime() val startedAt = SystemClock.elapsedRealtime()
return try { return try {
val result = block() val result = block()
val elapsedMs = SystemClock.elapsedRealtime() - startedAt val elapsedMs = SystemClock.elapsedRealtime() - startedAt
Timber.tag(TAG).d( Timber.tag(TAG).d("$functionName took ${elapsedMs}ms")
"$operation finished in ${elapsedMs}ms, " +
"fetched ${result.approximateSizeBytes()} bytes${result.itemCountLogText()}"
)
result result
} catch (error: CancellationException) { } catch (error: CancellationException) {
throw error throw error
} catch (error: Exception) { } catch (error: Exception) {
val elapsedMs = SystemClock.elapsedRealtime() - startedAt val elapsedMs = SystemClock.elapsedRealtime() - startedAt
Timber.tag(TAG).e(error, "$operation failed after ${elapsedMs}ms") Timber.tag(TAG).e(error, "$functionName failed after ${elapsedMs}ms")
throw error throw error
} }
} }
private fun Any?.approximateSizeBytes(): Int {
return toString().toByteArray(Charsets.UTF_8).size
}
private fun Any?.itemCountLogText(): String {
val count = when (this) {
is Collection<*> -> size
is Map<*, *> -> size
is Array<*> -> size
else -> null
}
return count?.let { ", items $it" }.orEmpty()
}
private fun PlaybackMethod.toJellyfinPlayMethod(): PlayMethod = when (this) { private fun PlaybackMethod.toJellyfinPlayMethod(): PlayMethod = when (this) {
PlaybackMethod.DIRECT_PLAY -> PlayMethod.DIRECT_PLAY PlaybackMethod.DIRECT_PLAY -> PlayMethod.DIRECT_PLAY
PlaybackMethod.DIRECT_STREAM -> PlayMethod.DIRECT_STREAM PlaybackMethod.DIRECT_STREAM -> PlayMethod.DIRECT_STREAM