Add a lot of code documentation (#1145)

## Description
A brain dump documenting many classes and functions throughout the app.
Definitely does not document everything, but covers most of the major
components. This should be useful for new contributors.

There is a small amount of code clean up too.

There are no user facing changes.

### Related issues
N/A

### Testing
N/A

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Ray 2026-03-29 10:52:25 -04:00 committed by GitHub
parent 2485d2c644
commit 66f060dccb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
92 changed files with 719 additions and 222 deletions

View file

@ -8,12 +8,18 @@ import com.github.damontecres.wholphin.ui.nav.Destination
import org.jellyfin.sdk.model.UUID
import org.jellyfin.sdk.model.api.ExtraType
/**
* Represents "extras" for media such as behind-the-scenes or deleted scenes
*/
sealed interface ExtrasItem {
val parentId: UUID
val type: ExtraType
val destination: Destination
val title: String?
/**
* Represents multiple extras of the same type
*/
data class Group(
override val parentId: UUID,
override val type: ExtraType,
@ -25,6 +31,9 @@ sealed interface ExtrasItem {
override val title: String? = null
}
/**
* Represents a single extra
*/
data class Single(
override val parentId: UUID,
override val type: ExtraType,
@ -38,6 +47,9 @@ sealed interface ExtrasItem {
}
}
/**
* Converts [ExtraType] to the string resource ID
*/
@get:StringRes
val ExtraType.stringRes: Int
get() =
@ -56,6 +68,9 @@ val ExtraType.stringRes: Int
ExtraType.SHORT -> R.string.shorts
}
/**
* Converts [ExtraType] to the plural resource ID
*/
@get:PluralsRes
val ExtraType.pluralRes: Int
get() =

View file

@ -50,6 +50,11 @@ val DefaultPlaylistItemsOptions =
DecadeFilter,
)
/**
* A way to filter libraries
*
* Gets and sets values within a [GetItemsFilter]
*/
sealed interface ItemFilterBy<T> {
@get:StringRes
val stringRes: Int

View file

@ -5,6 +5,14 @@ import org.jellyfin.sdk.model.extensions.ticks
import java.util.UUID
import kotlin.time.Duration
/**
* Represents audio or a song as a stripped down [BaseItem] since there may be a lot of these created.
*
* Typically added to a MediaItem as the tag for reference later
*
* The "key" can be used by a Compose LazyList key function as it will uniquely identify this particular
* audio even if the same song is added to the queue multiple times
*/
@Stable
data class AudioItem(
val key: Long = keyTracker++,

View file

@ -29,6 +29,9 @@ import java.util.Locale
import java.util.UUID
import kotlin.time.Duration
/**
* Wrapper for [BaseItemDto] with shortcuts for various UI elements
*/
@Serializable
@Stable
data class BaseItem(
@ -92,6 +95,9 @@ data class BaseItem(
@Transient
val timeRemainingOrRuntime: Duration? = data.timeRemaining ?: data.runTimeTicks?.ticks
/**
* Contains pre computed UI elements that would be expensive to create on the main thread
*/
@Transient
val ui =
BaseItemUi(
@ -178,6 +184,9 @@ data class BaseItem(
it.dayOfMonth.toString().padStart(2, '0')
}?.toIntOrNull()
/**
* Convert this [BaseItem] into a [Destination] to navigate to its page in the app
*/
fun destination(index: Int? = null): Destination {
if (destinationOverride != null) return destinationOverride
val result =
@ -228,6 +237,7 @@ data class BaseItem(
}
companion object {
@Deprecated("Use regular constructor instead")
fun from(
dto: BaseItemDto,
api: ApiClient,
@ -249,6 +259,9 @@ data class BaseItemUi(
val quickDetails: AnnotatedString,
)
/**
* Create the special [Destination.FilteredCollection] for the given genre information
*/
fun createGenreDestination(
genreId: UUID,
genreName: String,

View file

@ -7,6 +7,9 @@ import org.jellyfin.sdk.model.api.ImageType
import org.jellyfin.sdk.model.extensions.ticks
import kotlin.time.Duration
/**
* Represents a chapter within a video
*/
data class Chapter(
val name: String?,
val position: Duration,

View file

@ -16,6 +16,9 @@ import org.jellyfin.sdk.model.serializer.UUIDSerializer
import java.time.LocalDate
import java.util.UUID
/**
* The type of a Seerr/Discover object with mapping to the Jellyfin [BaseItemKind]
*/
@Serializable
enum class SeerrItemType(
val baseItemKind: BaseItemKind?,
@ -46,6 +49,9 @@ enum class SeerrItemType(
}
}
/**
* How available is a particular discovered item within the Jellyfin server
*/
@Serializable
enum class SeerrAvailability(
val status: Int,
@ -64,7 +70,7 @@ enum class SeerrAvailability(
}
/**
* An item provided by a discovery service (ie Seerr). It may exist on the JF server as well.
* An item provided by a discovery service (ie Seerr). It may exist on the JF server as well, see [availability].
*/
@Stable
@Serializable
@ -104,6 +110,9 @@ data class DiscoverItem(
}
}
/**
* A rating for a discovered item which is usually fetched separately from the item
*/
data class DiscoverRating(
val criticRating: Int?,
val audienceRating: Float?,

View file

@ -14,6 +14,9 @@ import org.jellyfin.sdk.model.api.request.GetPersonsRequest
import org.jellyfin.sdk.model.serializer.UUIDSerializer
import java.util.UUID
/**
* Filter for a collection folder
*/
@Serializable
data class CollectionFolderFilter(
val nameOverride: String? = null,
@ -24,6 +27,9 @@ data class CollectionFolderFilter(
val useSavedLibraryDisplayInfo: Boolean = true,
)
/**
* A sort of simplified filter which can be [applyTo] a [GetItemsRequest] or [GetPersonsRequest] to add or remove filters
*/
@Serializable
data class GetItemsFilter(
val favorite: Boolean? = null,
@ -52,7 +58,7 @@ data class GetItemsFilter(
}
/**
* Clear all of the values for the given filters
* Clear all the values for the given filters
*/
fun delete(filterOptions: List<ItemFilterBy<*>>): GetItemsFilter {
var newFilter = this
@ -128,6 +134,9 @@ data class GetItemsFilter(
isFavorite = favorite,
)
/**
* Merge another [GetItemsFilter] onto this one, replacing only unset values
*/
fun merge(filter: GetItemsFilter): GetItemsFilter =
this.copy(
favorite = favorite ?: filter.favorite,

View file

@ -13,6 +13,10 @@ import kotlinx.serialization.UseSerializers
import org.jellyfin.sdk.model.serializer.UUIDSerializer
import java.util.UUID
/**
* Store the media source and audio/subtitle tracks chosen for a specific media item
*
*/
@Entity(
foreignKeys = [
ForeignKey(

View file

@ -9,6 +9,11 @@ import kotlinx.serialization.UseSerializers
import org.jellyfin.sdk.model.serializer.UUIDSerializer
import java.util.UUID
/**
* Store modifications to an audio/subtitle track in a media item
*
* For example, the subtitle delay
*/
@Entity(
foreignKeys = [
ForeignKey(

View file

@ -17,6 +17,9 @@ import org.jellyfin.sdk.model.ServerVersion
import org.jellyfin.sdk.model.serializer.UUIDSerializer
import java.util.UUID
/**
* Represents a Jellyfin server
*/
@Entity(tableName = "servers")
@Serializable
data class JellyfinServer(
@ -29,6 +32,9 @@ data class JellyfinServer(
val serverVersion: ServerVersion? by lazy { version?.let(ServerVersion::fromString) }
}
/**
* Represents a Jellyfin user for a particular server
*/
@Entity(
tableName = "users",
foreignKeys = [
@ -59,6 +65,9 @@ data class JellyfinUser(
"JellyfinUser(rowId=$rowId, id=$id, name=$name, serverId=$serverId, accessToken?=${accessToken.isNotNullOrBlank()}, pin?=${pin.isNotNullOrBlank()})"
}
/**
* Represents the relationship between [JellyfinServer] and its [JellyfinUser]
*/
data class JellyfinServerUsers(
@Embedded val server: JellyfinServer,
@Relation(

View file

@ -18,6 +18,11 @@ import org.jellyfin.sdk.model.api.SortOrder
import org.jellyfin.sdk.model.serializer.UUIDSerializer
import java.util.UUID
/**
* Stores the filter, sort, and view options a user changes for a library
*
* This allows for restoring these settings whenever the user navigates to the library
*/
@Entity(
foreignKeys = [
ForeignKey(

View file

@ -9,6 +9,9 @@ enum class NavPinType {
UNPINNED,
}
/**
* Stores preference information about nav drawer items such as its order and whether to show or put in More
*/
@Entity(
foreignKeys = [
ForeignKey(

View file

@ -12,6 +12,9 @@ import org.jellyfin.sdk.model.api.BaseItemPerson
import org.jellyfin.sdk.model.api.ImageType
import org.jellyfin.sdk.model.api.PersonKind
/**
* Represents a person in some media such as an actor or director
*/
@Stable
data class Person(
val id: UUID,

View file

@ -5,6 +5,9 @@ import androidx.room.Entity
import org.jellyfin.sdk.model.api.BaseItemKind
import java.util.UUID
/**
* Store effects applied to some media such as color or hue adjustments
*/
@Entity(tableName = "playback_effects", primaryKeys = ["jellyfinUserRowId", "itemId", "type"])
data class PlaybackEffect(
val jellyfinUserRowId: Int,

View file

@ -9,6 +9,10 @@ import kotlinx.serialization.UseSerializers
import org.jellyfin.sdk.model.serializer.UUIDSerializer
import java.util.UUID
/**
* Stores the language choices for a series so they can be applied automatically to other episodes
* without the user needing to explicitly choose the tracks
*/
@Entity(
foreignKeys = [
ForeignKey(

View file

@ -6,6 +6,11 @@ import androidx.compose.runtime.setValue
import org.jellyfin.sdk.model.api.MediaType
import java.util.UUID
/**
* Tracks playback of multiple items. Points to the current media with function to advance or go to previous ones.
*
* This is not the same thing as a Jellyfin server playlist
*/
class Playlist(
items: List<BaseItem>,
startIndex: Int = 0,

View file

@ -2,6 +2,9 @@ package com.github.damontecres.wholphin.data.model
import com.github.damontecres.wholphin.services.SeerrUserConfig
/**
* Permission levels for a user on a Seerr server
*/
enum class SeerrPermission(
private val flag: Int,
) {

View file

@ -13,6 +13,9 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import org.jellyfin.sdk.model.serializer.UUIDSerializer
/**
* Represents a Seerr server instance
*/
@Entity(
tableName = "seerr_servers",
indices = [Index("url", unique = true)],
@ -26,6 +29,9 @@ data class SeerrServer(
val version: String? = null,
)
/**
* Represents a user on a [SeerrServer]
*/
@Entity(
tableName = "seerr_users",
foreignKeys = [
@ -56,12 +62,18 @@ data class SeerrUser(
"SeerrUser(jellyfinUserRowId=$jellyfinUserRowId, serverId=$serverId, authMethod=$authMethod, username=$username, password?=${password.isNotNullOrBlank()}, credential?=${credential.isNotNullOrBlank()})"
}
/**
* The method used to authenticate a user to the server
*/
enum class SeerrAuthMethod {
LOCAL,
JELLYFIN,
API_KEY,
}
/**
* Represents the relationship between a [SeerrServer] and its [SeerrUser]s
*/
data class SeerrServerUsers(
@Embedded val server: SeerrServer,
@Relation(

View file

@ -1,9 +1,15 @@
package com.github.damontecres.wholphin.data.model
/**
* Represents a trailer for media
*/
sealed interface Trailer {
val name: String
}
/**
* A [Trailer] stored on the Jellyfin server
*/
data class LocalTrailer(
val baseItem: BaseItem,
) : Trailer {
@ -11,6 +17,9 @@ data class LocalTrailer(
get() = baseItem.name ?: ""
}
/**
* A [Trailer] available via a remote URL, such as YouTube
*/
data class RemoteTrailer(
override val name: String,
val url: String,

View file

@ -14,7 +14,7 @@ import androidx.media3.effect.ScaleAndRotateTransformation
import androidx.room.Ignore
/**
* Modifications to a video playback
* Modifications to an image or video playback
*/
data class VideoFilter(
val rotation: Int = 0,