Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ kotlin {
implementation(projects.feature.credits)
implementation(projects.feature.details)
implementation(projects.feature.discover)
implementation(projects.feature.episode)
implementation(projects.feature.home)
implementation(projects.feature.lists)
implementation(projects.feature.mediaLists)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.divinelink.feature.details.navigation.detailsScreen
import com.divinelink.feature.details.navigation.personScreen
import com.divinelink.feature.details.navigation.posterScreen
import com.divinelink.feature.discover.ui.navigation.discoverScreen
import com.divinelink.feature.episode.ui.navigation.episodeScreen
import com.divinelink.feature.home.navigation.homeScreen
import com.divinelink.feature.lists.create.ui.navigation.createListScreen
import com.divinelink.feature.lists.create.ui.navigation.editListScreen
Expand Down Expand Up @@ -297,6 +298,14 @@ val navigationModule = module {
}
}

single<NavGraphExtension>(named<Navigation.EpisodeRoute>()) {
{ navController, _ ->
episodeScreen(
onNavigate = navController::findNavigation,
)
}
}

single<List<NavGraphExtension>> {
getAll<NavGraphExtension>()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.divinelink.feature.details.media.ui.DetailsViewModel
import com.divinelink.feature.details.person.ui.PersonViewModel
import com.divinelink.feature.discover.DiscoverViewModel
import com.divinelink.feature.discover.filters.SelectFilterViewModel
import com.divinelink.feature.episode.EpisodeViewModel
import com.divinelink.feature.home.HomeViewModel
import com.divinelink.feature.lists.create.CreateListViewModel
import com.divinelink.feature.lists.create.backdrop.SelectBackdropViewModel
Expand Down Expand Up @@ -57,6 +58,7 @@ val appViewModelModule = module {
viewModelOf(::SelectFilterViewModel)
viewModelOf(::MediaListsViewModel)
viewModelOf(::SeasonViewModel)
viewModelOf(::EpisodeViewModel)

// Components
viewModelOf(::SwitchViewButtonViewModel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.divinelink.core.navigation.route.navigateToCreateList
import com.divinelink.core.navigation.route.navigateToDetails
import com.divinelink.core.navigation.route.navigateToDiscover
import com.divinelink.core.navigation.route.navigateToEditList
import com.divinelink.core.navigation.route.navigateToEpisode
import com.divinelink.core.navigation.route.navigateToListDetails
import com.divinelink.core.navigation.route.navigateToLists
import com.divinelink.core.navigation.route.navigateToMediaLists
Expand All @@ -34,6 +35,10 @@ import com.divinelink.feature.settings.navigation.settings.navigateToSettings
fun NavController.findNavigation(route: Navigation) {
when (route) {
Navigation.Back -> navigateUp()
Navigation.TwiceBack -> {
navigateUp()
navigateUp()
}
Navigation.AboutSettingsRoute -> navigateToAboutSettings()
Navigation.AccountSettingsRoute -> navigateToAccountSettings()
Navigation.DetailsPreferencesSettingsRoute -> navigateToDetailsPreferenceSettings()
Expand Down Expand Up @@ -64,6 +69,7 @@ fun NavController.findNavigation(route: Navigation) {
is Navigation.MediaPosterRoute -> navigateToPoster(route)
is Navigation.MediaListsRoute -> navigateToMediaLists(route)
is Navigation.SeasonRoute -> navigateToSeason(route)
is Navigation.EpisodeRoute -> navigateToEpisode(route)

// This is from top level navigation
Navigation.HomeRoute -> Unit
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.divinelink.scenepeek.feature.episode

@Previews
@Composable
fun EpisodeContentScreenshots(
@PreviewParameter(EpisodeUiStateParameterProvider::class) uiState: EpisodeUiState,
) {
EpisodeContentPreview(uiState)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.divinelink.core.data.media.repository

import com.divinelink.core.model.Genre
import com.divinelink.core.model.PaginationData
import com.divinelink.core.model.details.Episode
import com.divinelink.core.model.details.Season
import com.divinelink.core.model.details.SeasonDetails
import com.divinelink.core.model.discover.DiscoverFilter
Expand Down Expand Up @@ -98,4 +99,15 @@ interface MediaRepository {
showId: Int,
season: Int,
): Flow<Resource<SeasonDetails?>>

fun fetchEpisode(
showId: Int,
season: Int,
number: Int,
): Flow<Result<Episode>>

fun getSeasonEpisodesNumber(
showId: Int,
season: Int,
): Result<List<Int>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.divinelink.core.database.media.mapper.map
import com.divinelink.core.database.person.PersonDao
import com.divinelink.core.model.Genre
import com.divinelink.core.model.PaginationData
import com.divinelink.core.model.details.Episode
import com.divinelink.core.model.details.Season
import com.divinelink.core.model.details.SeasonDetails
import com.divinelink.core.model.discover.DiscoverFilter
Expand All @@ -33,6 +34,7 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext

class ProdMediaRepository(
private val remote: MediaService,
Expand Down Expand Up @@ -274,9 +276,8 @@ class ProdMediaRepository(
}
}

override fun fetchTvSeasons(id: Int): Flow<Result<List<Season>>> = dao.fetchSeasons(id).map {
Result.success(it)
}
override fun fetchTvSeasons(id: Int): Flow<Result<List<Season>>> = dao.fetchSeasons(id)
.map { runCatching { it } }

override fun fetchSeason(
showId: Int,
Expand Down Expand Up @@ -320,25 +321,62 @@ class ProdMediaRepository(
).map { it.map() }
},
saveFetchResult = { result ->
val data = result.data
withContext(dispatcher.io) {
val data = result.data

dao.insertSeasonDetails(
seasonDetails = data,
showId = showId,
seasonNumber = season,
)

dao.insertEpisodes(data.episodes)

data.episodes.forEach { episode ->
personDao.insertGuestStars(
season = season,
dao.insertSeasonDetails(
seasonDetails = data,
showId = showId,
episode = episode.number,
guestStars = data.guestStars,
seasonNumber = season,
)

dao.insertEpisodes(data.episodes)

data.episodes.forEach { episode ->
personDao.insertGuestStars(
season = season,
showId = showId,
episode = episode.number,
guestStars = episode.guestStars,
)
}
}
},
shouldFetch = { true },
)

override fun getSeasonEpisodesNumber(
showId: Int,
season: Int,
): Result<List<Int>> = runCatching {
dao.fetchSeasonEpisodesCount(
showId = showId,
season = season,
)
}

override fun fetchEpisode(
showId: Int,
season: Int,
number: Int,
): Flow<Result<Episode>> = combine(
personDao.fetchEpisodeGuestStars(
showId = showId,
episode = number,
season = season,
),
flowOf(
dao.fetchEpisode(
showId = showId,
episodeNumber = number,
seasonNumber = season,
),
),
) { guestStars, episode ->
runCatching {
episode.copy(
guestStars = guestStars,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,9 @@ interface MediaDao {
season: Int,
showId: Int,
): Flow<SeasonDetailsEntity?>

fun fetchSeasonEpisodesCount(
season: Int,
showId: Int,
): List<Int>
}
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ class ProdMediaDao(
airDate = episode.airDate,
stillPath = episode.stillPath,
voteAverage = episode.voteAverage?.toDouble() ?: 0.0,
voteCount = episode.voteCount?.toLong() ?: 0,
),
)
}
Expand Down Expand Up @@ -387,4 +388,19 @@ class ProdMediaDao(
.asFlow()
.mapToOneOrNull(dispatcher.io)
}

override fun fetchSeasonEpisodesCount(
season: Int,
showId: Int,
): List<Int> = database
.transactionWithResult {
database
.episodeEntityQueries
.countSeasonEpisodes(
showId = showId.toLong(),
seasonNumber = season.toLong(),
)
.executeAsList()
.map { it.toInt() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fun EpisodeEntity.map() = Episode(
showId = showId.toInt(),
stillPath = stillPath,
voteAverage = voteAverage.toString(),
voteCount = voteCount.toInt(),
crew = emptyList(),
guestStars = emptyList(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@ interface PersonDao {
showId: Int,
season: Int,
): Flow<List<Person>>

fun fetchEpisodeGuestStars(
showId: Int,
season: Int,
episode: Int,
): Flow<List<Person>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ class ProdPersonDao(
val insertedPersonIds = mutableSetOf<Long>()
val insertedCreditIds = mutableSetOf<String>()

// TODO Fix guest star insertion, only adds guest star to the last episode of the season.
guestStars.forEach { person ->
if (person.id !in insertedPersonIds) {
database.personEntityQueries.insertPerson(
Expand Down Expand Up @@ -182,12 +183,12 @@ class ProdPersonDao(
),
)

database.seasonGuestStarRoleEntityQueries.insertSeasonGuestStarRole(
database.episodeGuestStarEntityQueries.insertGuestStar(
showId = showId.toLong(),
season = season.toLong(),
creditId = role.creditId,
episode = episode.toLong(),
episodeCount = role.totalEpisodes?.toLong(),
// episodeCount = role.totalEpisodes?.toLong(),
displayOrder = role.order?.toLong() ?: -1,
)
insertedCreditIds += role.creditId
Expand All @@ -203,7 +204,7 @@ class ProdPersonDao(
): Flow<List<Person>> = database
.transactionWithResult {
database
.seasonGuestStarRoleEntityQueries
.episodeGuestStarEntityQueries
.fetchSeasonGuestStars(
season = season.toLong(),
showId = showId.toLong(),
Expand All @@ -225,8 +226,47 @@ class ProdPersonDao(
PersonRole.SeriesActor(
character = firstRole.character,
creditId = firstRole.creditId,
totalEpisodes = firstRole.episodeCount?.toInt(),
order = firstRole.displayOrder.toInt(),
totalEpisodes = firstRole.totalEpisodes.toInt(),
order = firstRole.displayOrder?.toInt(),
),
),
)
}
}
}

override fun fetchEpisodeGuestStars(
showId: Int,
season: Int,
episode: Int,
): Flow<List<Person>> = database
.transactionWithResult {
database
.episodeGuestStarEntityQueries
.fetchEpisodeGuestStars(
season = season.toLong(),
showId = showId.toLong(),
episode = episode.toLong(),
)
.asFlow()
.mapToList(dispatcher.io)
.map { entities ->
entities
.groupBy { it.id }
.map { (personId, roles) ->
val firstRole = roles.first()
Person(
id = personId,
name = firstRole.name,
profilePath = firstRole.profilePath,
gender = Gender.from(firstRole.gender.toInt()),
knownForDepartment = firstRole.knownForDepartment,
role = listOf(
PersonRole.SeriesActor(
character = firstRole.character,
creditId = firstRole.creditId,
totalEpisodes = null,
order = firstRole.displayOrder?.toInt(),
),
),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
CREATE TABLE EpisodeEntity (
id INTEGER NOT NULL,
showId INTEGER NOT NULL,
overview TEXT NOT NULL,
overview TEXT,
name TEXT NOT NULL,
runtime TEXT,
episodeNumber INTEGER NOT NULL,
seasonNumber INTEGER NOT NULL,
airDate TEXT,
stillPath TEXT,
voteAverage REAL NOT NULL,
voteCount INTEGER NOT NULL,
UNIQUE(id, seasonNumber, showId)
);

Expand All @@ -23,6 +24,7 @@ INSERT OR REPLACE INTO EpisodeEntity (
seasonNumber,
airDate,
stillPath,
voteCount,
voteAverage
)
VALUES ?;
Expand All @@ -40,3 +42,8 @@ fetchEpisode:
SELECT * FROM EpisodeEntity
WHERE showId = ? AND seasonNumber = ? AND episodeNumber = ?
LIMIT 1;

countSeasonEpisodes:
SELECT episodeNumber
FROM EpisodeEntity
WHERE showId = ? AND seasonNumber = ?;
Loading