This commit is contained in:
Damontecres 2025-09-23 20:03:25 -04:00
parent bd75539cb6
commit 913334b43d
No known key found for this signature in database
24 changed files with 41 additions and 117 deletions

View file

@ -24,6 +24,7 @@ import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Surface
import com.github.damontecres.dolphin.data.ServerRepository
import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.ui.CoilConfig
import com.github.damontecres.dolphin.ui.ServerLoginPage
import com.github.damontecres.dolphin.ui.nav.ApplicationContent
import com.github.damontecres.dolphin.ui.theme.DolphinTheme

View file

@ -8,6 +8,6 @@ import androidx.room.RoomDatabase
version = 2,
exportSchema = false,
)
abstract class DolphinDatabase : RoomDatabase() {
abstract class AppDatabase : RoomDatabase() {
abstract fun serverDao(): JellyfinServerDao
}

View file

@ -19,7 +19,7 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object DolphinModule {
object AppModule {
@Provides
@Singleton
fun okHttpClient() =

View file

@ -6,7 +6,7 @@ import androidx.datastore.core.DataStoreFactory
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.dataStoreFile
import androidx.room.Room
import com.github.damontecres.dolphin.data.DolphinDatabase
import com.github.damontecres.dolphin.data.AppDatabase
import com.github.damontecres.dolphin.data.JellyfinServerDao
import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.preferences.UserPreferencesSerializer
@ -24,18 +24,18 @@ object DatabaseModule {
@Singleton
fun database(
@ApplicationContext context: Context,
): DolphinDatabase =
): AppDatabase =
Room
.databaseBuilder(
context,
DolphinDatabase::class.java,
AppDatabase::class.java,
"dolphin",
).fallbackToDestructiveMigration(false)
.build()
@Provides
@Singleton
fun serverDao(db: DolphinDatabase): JellyfinServerDao = db.serverDao()
fun serverDao(db: AppDatabase): JellyfinServerDao = db.serverDao()
@Provides
@Singleton

View file

@ -1,4 +1,4 @@
package com.github.damontecres.dolphin
package com.github.damontecres.dolphin.ui
import androidx.compose.runtime.Composable
import coil3.ImageLoader

View file

@ -1,4 +1,4 @@
package com.github.damontecres.dolphin
package com.github.damontecres.dolphin.ui
import android.view.KeyEvent
import androidx.compose.foundation.MarqueeAnimationMode

View file

@ -1,31 +0,0 @@
package com.github.damontecres.dolphin.ui.cards
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.tv.material3.Text
import com.github.damontecres.dolphin.data.model.Library
import com.github.damontecres.dolphin.data.model.Video
import org.jellyfin.sdk.model.api.BaseItemDto
@Composable
fun DolphinCard(
item: Any?,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
when (item) {
is Video ->
VideoCard(
item = item,
onClick = onClick,
modifier = modifier,
)
is Library -> TODO()
is BaseItemDto -> {
Text(item.id.toString())
}
null -> NullCard(modifier = modifier)
}
}

View file

@ -41,8 +41,8 @@ import androidx.tv.material3.Text
import coil3.compose.AsyncImage
import com.github.damontecres.dolphin.R
import com.github.damontecres.dolphin.data.model.BaseItem
import com.github.damontecres.dolphin.enableMarquee
import com.github.damontecres.dolphin.ui.FontAwesome
import com.github.damontecres.dolphin.ui.enableMarquee
import kotlinx.coroutines.delay
import org.jellyfin.sdk.model.api.BaseItemKind

View file

@ -1,48 +0,0 @@
package com.github.damontecres.dolphin.ui.cards
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.tv.material3.Card
import androidx.tv.material3.Text
import coil3.compose.AsyncImage
import com.github.damontecres.dolphin.data.model.Video
@Composable
fun VideoCard(
item: Video,
onClick: () -> Unit,
modifier: Modifier = Modifier,
cardWidth: Dp = 150.dp,
cardHeight: Dp = 200.dp,
) {
Card(
modifier = modifier,
onClick = onClick,
) {
Column(
verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier.width(cardWidth),
) {
AsyncImage(
model = item.imageUrl,
contentDescription = item.name,
contentScale = ContentScale.Fit,
modifier =
Modifier
.fillMaxWidth()
.height(cardHeight),
)
Text(
text = item.name ?: "",
)
}
}
}

View file

@ -42,16 +42,16 @@ import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import com.github.damontecres.dolphin.R
import com.github.damontecres.dolphin.data.model.BaseItem
import com.github.damontecres.dolphin.ifElse
import com.github.damontecres.dolphin.tryRequestFocus
import com.github.damontecres.dolphin.ui.AppColors
import com.github.damontecres.dolphin.ui.FontAwesome
import com.github.damontecres.dolphin.ui.cards.ItemCard
import com.github.damontecres.dolphin.ui.ifElse
import com.github.damontecres.dolphin.ui.nav.NavigationManager
import com.github.damontecres.dolphin.ui.playback.isBackwardButton
import com.github.damontecres.dolphin.ui.playback.isForwardButton
import com.github.damontecres.dolphin.ui.playback.isPlayKeyUp
import com.github.damontecres.dolphin.util.DolphinPager
import com.github.damontecres.dolphin.ui.tryRequestFocus
import com.github.damontecres.dolphin.util.ItemPager
import kotlinx.coroutines.launch
import timber.log.Timber
import kotlin.math.max
@ -60,7 +60,7 @@ private const val DEBUG = false
@Composable
fun CardGrid(
pager: DolphinPager,
pager: ItemPager,
itemOnClick: (BaseItem) -> Unit,
longClicker: (BaseItem) -> Unit,
letterPosition: suspend (Char) -> Int,

View file

@ -10,13 +10,13 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import androidx.tv.material3.Text
import com.github.damontecres.dolphin.OneTimeLaunchedEffect
import com.github.damontecres.dolphin.data.model.BaseItem
import com.github.damontecres.dolphin.data.model.Library
import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.ui.OneTimeLaunchedEffect
import com.github.damontecres.dolphin.ui.nav.Destination
import com.github.damontecres.dolphin.ui.nav.NavigationManager
import com.github.damontecres.dolphin.util.DolphinPager
import com.github.damontecres.dolphin.util.ItemPager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@ -37,7 +37,7 @@ class CollectionFolderViewModel
constructor(
api: ApiClient,
) : ItemViewModel<Library>(api) {
val pager = MutableLiveData<DolphinPager?>()
val pager = MutableLiveData<ItemPager?>()
override fun init(
itemId: UUID,
@ -63,7 +63,7 @@ class CollectionFolderViewModel
sortOrder = listOf(SortOrder.ASCENDING),
fields = listOf(ItemFields.PRIMARY_IMAGE_ASPECT_RATIO),
)
val newPager = DolphinPager(api, request, viewModelScope)
val newPager = ItemPager(api, request, viewModelScope)
newPager.init()
pager.value = newPager
}
@ -124,7 +124,7 @@ fun TVShowCollectionDetails(
navigationManager: NavigationManager,
library: Library,
item: BaseItem,
pager: DolphinPager,
pager: ItemPager,
modifier: Modifier = Modifier,
) {
val gridFocusRequester = remember { FocusRequester() }

View file

@ -17,7 +17,7 @@ import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.ui.cards.ItemRow
import com.github.damontecres.dolphin.ui.nav.Destination
import com.github.damontecres.dolphin.ui.nav.NavigationManager
import com.github.damontecres.dolphin.util.DolphinPager
import com.github.damontecres.dolphin.util.ItemPager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@ -54,7 +54,8 @@ class SeasonViewModel
sortOrder = listOf(SortOrder.ASCENDING),
fields = listOf(ItemFields.PRIMARY_IMAGE_ASPECT_RATIO, ItemFields.CHILD_COUNT),
)
val pager = DolphinPager(api, request, viewModelScope, itemCount = item.data.childCount)
val pager =
ItemPager(api, request, viewModelScope, itemCount = item.data.childCount)
pager.init()
episodes.value = pager
}

View file

@ -17,7 +17,7 @@ import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.ui.cards.ItemRow
import com.github.damontecres.dolphin.ui.nav.Destination
import com.github.damontecres.dolphin.ui.nav.NavigationManager
import com.github.damontecres.dolphin.util.DolphinPager
import com.github.damontecres.dolphin.util.ItemPager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@ -59,7 +59,8 @@ class SeriesViewModel
ItemFields.SEASON_USER_DATA,
),
)
val pager = DolphinPager(api, request, viewModelScope, itemCount = item.data.childCount)
val pager =
ItemPager(api, request, viewModelScope, itemCount = item.data.childCount)
pager.init()
seasons.value = pager
}

View file

@ -17,10 +17,10 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.github.damontecres.dolphin.data.model.BaseItem
import com.github.damontecres.dolphin.isNotNullOrBlank
import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.ui.DefaultItemFields
import com.github.damontecres.dolphin.ui.cards.ItemRow
import com.github.damontecres.dolphin.ui.isNotNullOrBlank
import com.github.damontecres.dolphin.ui.nav.Destination
import com.github.damontecres.dolphin.ui.nav.NavigationManager
import dagger.hilt.android.lifecycle.HiltViewModel

View file

@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.debounce
class ControllerViewState internal constructor(
@IntRange(from = 0)
@param:IntRange(from = 0)
private val hideMilliseconds: Int,
val controlsEnabled: Boolean,
) {
@ -31,7 +31,6 @@ class ControllerViewState internal constructor(
}
fun pulseControls(milliseconds: Int = hideMilliseconds) {
// Log.i("PlaybackPageContent", "pulseControls=$milliseconds")
channel.trySend(milliseconds)
}
@ -41,7 +40,6 @@ class ControllerViewState internal constructor(
.consumeAsFlow()
.debounce { it.toLong() }
.collect {
// Log.i("PlaybackPageContent", "collect")
_controlsVisible = false
}
}

View file

@ -45,9 +45,9 @@ import androidx.media3.ui.compose.state.rememberPresentationState
import androidx.media3.ui.compose.state.rememberPreviousButtonState
import androidx.tv.material3.MaterialTheme
import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.tryRequestFocus
import com.github.damontecres.dolphin.ui.nav.Destination
import com.github.damontecres.dolphin.ui.nav.NavigationManager
import com.github.damontecres.dolphin.ui.tryRequestFocus
import com.github.damontecres.stashapp.ui.components.playback.SkipIndicator
import com.github.damontecres.stashapp.ui.components.playback.rememberSeekBarState
import org.jellyfin.sdk.model.api.DeviceProfile

View file

@ -57,8 +57,9 @@ import androidx.tv.material3.ListItem
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import com.github.damontecres.dolphin.R
import com.github.damontecres.dolphin.tryRequestFocus
import com.github.damontecres.dolphin.ui.AppColors
import com.github.damontecres.dolphin.ui.tryRequestFocus
import com.github.damontecres.dolphin.util.TrackSupport
import com.github.damontecres.stashapp.ui.components.playback.SeekBarImpl
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive

View file

@ -16,6 +16,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.media3.common.Player
import androidx.tv.material3.Text
import com.github.damontecres.dolphin.util.TrackSupport
@Composable
fun PlaybackOverlay(

View file

@ -9,6 +9,7 @@ import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import com.github.damontecres.dolphin.ui.nav.Destination
import com.github.damontecres.dolphin.util.TrackActivityPlaybackListener
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers

View file

@ -41,7 +41,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.unit.dp
import androidx.tv.material3.MaterialTheme
import com.github.damontecres.dolphin.handleDPadKeyEvents
import com.github.damontecres.dolphin.ui.handleDPadKeyEvents
import com.github.damontecres.dolphin.ui.playback.ControllerViewState
@Composable

View file

@ -22,7 +22,7 @@ import androidx.compose.ui.unit.sp
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import com.github.damontecres.dolphin.R
import com.github.damontecres.dolphin.ifElse
import com.github.damontecres.dolphin.ui.ifElse
import kotlin.math.abs
@Composable

View file

@ -18,7 +18,7 @@ import org.jellyfin.sdk.api.client.extensions.itemsApi
import org.jellyfin.sdk.model.api.request.GetItemsRequest
import timber.log.Timber
class DolphinPager(
class ItemPager(
val api: ApiClient,
val request: GetItemsRequest,
private val scope: CoroutineScope,
@ -101,8 +101,7 @@ class DolphinPager(
}
companion object {
private const val TAG = "DolphinPager"
private const val DEBUG = true
private const val DEBUG = false
}
class ItemList<T>(

View file

@ -1,4 +1,4 @@
package com.github.damontecres.dolphin.ui.playback
package com.github.damontecres.dolphin.util
import androidx.annotation.OptIn
import androidx.media3.common.Player
@ -64,7 +64,7 @@ class TrackActivityPlaybackListener(
}
timestamp = now
} catch (ex: Exception) {
Timber.w(ex, "Exception during track activity timer")
Timber.Forest.w(ex, "Exception during track activity timer")
}
}
}
@ -98,7 +98,7 @@ class TrackActivityPlaybackListener(
override fun onPlaybackStateChanged(playbackState: Int) {
if (playbackState == Player.STATE_ENDED) {
Timber.v("onPlaybackStateChanged STATE_ENDED")
Timber.Forest.v("onPlaybackStateChanged STATE_ENDED")
// TODO mark as watched
}
}
@ -108,7 +108,7 @@ class TrackActivityPlaybackListener(
val totalDuration = totalPlayDurationMilliseconds.get()
val calcPosition =
(if (position >= 0) position else player.currentPosition).milliseconds
Timber.v("saveActivity: itemId=$itemId, pos=$calcPosition")
Timber.Forest.v("saveActivity: itemId=$itemId, pos=$calcPosition")
// TODO parameters
api.playStateApi.reportPlaybackProgress(
PlaybackProgressInfo(

View file

@ -1,4 +1,4 @@
package com.github.damontecres.dolphin.ui.playback
package com.github.damontecres.dolphin.util
import android.content.Context
import androidx.annotation.OptIn