Skip to content

Commit

Permalink
#81 Refactor trakt Add and Remove history
Browse files Browse the repository at this point in the history
  • Loading branch information
moallemi authored Jun 16, 2024
2 parents 626c4ff + a5e903d commit 0c3e08f
Show file tree
Hide file tree
Showing 36 changed files with 560 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ enum class TmdbType {

interface TraktSearchRemoteSource {

suspend fun getByTmdbId(id: String, type: TmdbType? = null): Result<Long, GeneralError>
suspend fun getByTmdbId(id: Int, type: TmdbType? = null): Result<Int, GeneralError>
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import javax.inject.Inject
class TraktSearchRemoteSourceImpl @Inject constructor(
private val traktIDLookupService: TraktSearchService,
) : TraktSearchRemoteSource {
override suspend fun getByTmdbId(id: String, type: TmdbType?): Result<Long, GeneralError> {

override suspend fun getByTmdbId(id: Int, type: TmdbType?): Result<Int, GeneralError> {
return when (val result = traktIDLookupService.movieIDLookup(idType = "tmdb", id = id)) {
is NetworkResponse.ApiError -> {
val errorResponse = result.body
Expand All @@ -18,7 +19,7 @@ class TraktSearchRemoteSourceImpl @Inject constructor(
is NetworkResponse.NetworkError -> Result.Failure(GeneralError.NetworkError)
is NetworkResponse.Success -> {
val body = result.body ?: emptyList()
val movieItemId = body.find { it.movie.ids.tmdb == id.toLong() }?.movie?.ids?.trakt ?: -1
val movieItemId = body.find { it.movie.ids.tmdb == id }?.movie?.ids?.trakt ?: -1
return Result.Success(movieItemId)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ interface TraktSyncRemoteSource {

suspend fun getAllHistories(): Result<Nothing, GeneralError>

suspend fun getHistoryById(id: String): Result<Boolean, GeneralError>
suspend fun getHistoryById(id: Int): Result<Boolean, GeneralError>

suspend fun addToHistory(id: String): Result<Unit, GeneralError>
suspend fun addToHistory(id: Int): Result<Unit, GeneralError>

suspend fun removeFromHistory(id: Int): Result<Unit, GeneralError>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package io.filmtime.data.api.trakt
import io.filmtime.data.model.GeneralError
import io.filmtime.data.model.Result
import io.filmtime.data.network.adapter.NetworkResponse
import io.filmtime.data.network.trakt.AddHistoryRequest
import io.filmtime.data.network.trakt.HistoryIDS
import io.filmtime.data.network.trakt.MovieHistory
import io.filmtime.data.network.trakt.SyncHistoryRequest
import io.filmtime.data.network.trakt.TraktSyncService
import io.filmtime.data.storage.trakt.TraktAuthLocalSource
import kotlinx.coroutines.flow.firstOrNull
Expand All @@ -31,7 +31,7 @@ class TraktSyncRemoteSourceImpl
}
}

override suspend fun getHistoryById(id: String): Result<Boolean, GeneralError> {
override suspend fun getHistoryById(id: Int): Result<Boolean, GeneralError> {
// TODO: move check token in a function
val tokens =
traktAuthLocalSource.tokens.firstOrNull() ?: return Result.Failure(GeneralError.ApiError("Unauthorized", 401))
Expand All @@ -45,32 +45,55 @@ class TraktSyncRemoteSourceImpl
is NetworkResponse.NetworkError -> Result.Failure(GeneralError.NetworkError)
is NetworkResponse.UnknownError -> Result.Failure(GeneralError.UnknownError(result.error))
is NetworkResponse.Success -> {
val watched = result.body?.any { it.movie.ids.trakt == id.toLong() } ?: false
val watched = result.body?.any { it.movie.ids.trakt == id } ?: false
Result.Success(watched)
}
}
}

override suspend fun addToHistory(id: String): Result<Unit, GeneralError> {
override suspend fun addToHistory(id: Int): Result<Unit, GeneralError> {
val tokens =
traktAuthLocalSource.tokens.firstOrNull() ?: return Result.Failure(GeneralError.ApiError("Unauthorized", 401))
val result = traktSyncService.addMovieToHistory(
accessToken = "Bearer " + tokens.accessToken,
body = AddHistoryRequest(
body = SyncHistoryRequest(
movies = listOf(
MovieHistory(
ids = HistoryIDS(
trakt = id.toLong(),
trakt = id,
),
),
),
),
)
return when (result) {
is NetworkResponse.Success -> Result.Success(Unit)
is NetworkResponse.ApiError -> Result.Failure(GeneralError.ApiError(result.body.error, result.code))
is NetworkResponse.NetworkError -> Result.Failure(GeneralError.NetworkError)
is NetworkResponse.UnknownError -> Result.Failure(GeneralError.UnknownError(result.error))
}
}

override suspend fun removeFromHistory(id: Int): Result<Unit, GeneralError> {
val tokens =
traktAuthLocalSource.tokens.firstOrNull() ?: return Result.Failure(GeneralError.ApiError("Unauthorized", 401))
val result = traktSyncService.removeMovieFromHistory(
accessToken = "Bearer " + tokens.accessToken,
body = SyncHistoryRequest(
movies = listOf(
MovieHistory(
ids = HistoryIDS(
trakt = id,
),
),
),
),
)
return when (result) {
is NetworkResponse.Success -> Result.Success(Unit)
is NetworkResponse.ApiError -> Result.Failure(GeneralError.ApiError(result.body.error, result.code))
is NetworkResponse.NetworkError -> Result.Failure(GeneralError.NetworkError)
is NetworkResponse.UnknownError -> Result.Failure(GeneralError.UnknownError(result.error))
}
}
}
41 changes: 41 additions & 0 deletions data/model/src/main/java/io/filmtime/data/model/Result.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,45 @@ package io.filmtime.data.model
sealed class Result<out S, out E> {
data class Success<out S>(val data: S) : Result<S, Nothing>()
data class Failure<out E>(val error: E) : Result<Nothing, E>()

suspend fun <T> mapSuccess(transform: suspend (S) -> T): Result<T, E> {
return when (this) {
is Result.Failure -> this
is Success -> Success(transform(data))
}
}

suspend fun <T> mapFailure(transform: suspend (E) -> T): Result<S, T> {
return when (this) {
is Result.Failure -> Failure(transform(error))
is Success -> this
}
}

inline fun fold(
onSuccess: (S) -> Unit,
onFailure: (E) -> Unit,
onCompletion: () -> Unit = {},
) {
when (this) {
is Result.Failure -> onFailure(this.error)
is Success -> onSuccess(this.data)
}

onCompletion()
}

fun successValue(): S? {
return when (this) {
is Success -> this.data
is Result.Failure -> null
}
}

fun errorValue(): E? {
return when (this) {
is Success -> null
is Result.Failure -> this.error
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.filmtime.data.model

data class TraktHistory(
val traktId: Int,
val isWatched: Boolean = false,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class AddHistoryRequest(
data class SyncHistoryRequest(
val movies: List<MovieHistory>? = null,
val shows: List<Show>? = null,
)
Expand All @@ -20,10 +20,10 @@ data class MovieHistory(

@Serializable
data class HistoryIDS(
val trakt: Long? = null,
val tvdb: Long? = null,
val trakt: Int? = null,
val tvdb: Int? = null,
val imdb: String? = null,
val tmdb: Long? = null,
val tmdb: Int? = null,
val slug: String? = null,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package io.filmtime.data.network.trakt
import kotlinx.serialization.Serializable

@Serializable
data class AddToHistoryResponse(
val added: Added,
data class SyncHistoryResponse(
val added: SyncHistoryDto? = null,
val deleted: SyncHistoryDto? = null,
)

@Serializable
data class Added(
data class SyncHistoryDto(
val movies: Long,
val episodes: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ data class Movie(

@Serializable
data class IDS(
val trakt: Long,
val trakt: Int,
val slug: String,
val imdb: String,
val tmdb: Long,
val tmdb: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ interface TraktSearchService {
@GET("search/{id_type}/{id}?type=movie")
suspend fun movieIDLookup(
@Path("id_type") idType: String,
@Path("id") id: String,
@Path("id") id: Int,
): NetworkResponse<List<TraktMovieIDLookupResponse>, TraktErrorResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query

interface TraktSyncService {

Expand All @@ -18,13 +19,20 @@ interface TraktSyncService {
@GET("sync/history/{type}/{id}")
suspend fun getHistoryById(
@Path("type") type: String,
@Path("id") id: String,
@Path("id") id: Int,
@Header("Authorization") accessToken: String,
@Query("limit") limit: Int = 240,
): NetworkResponse<List<HistoryMovie>, TraktErrorResponse>

@POST("sync/history")
suspend fun addMovieToHistory(
@Header("Authorization") accessToken: String,
@Body body: AddHistoryRequest,
): NetworkResponse<AddToHistoryResponse, TraktErrorResponse>
@Body body: SyncHistoryRequest,
): NetworkResponse<SyncHistoryResponse, TraktErrorResponse>

@POST("sync/history/remove")
suspend fun removeMovieFromHistory(
@Header("Authorization") accessToken: String,
@Body body: SyncHistoryRequest,
): NetworkResponse<SyncHistoryResponse, TraktErrorResponse>
}
1 change: 0 additions & 1 deletion data/tmdb-movies/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ android {

dependencies {
implementation(project(":data:api:tmdb"))
implementation(project(":data:api:trakt"))

api(libs.paging.runtime)
implementation(project(":data:database"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import io.filmtime.data.api.tmdb.TmdbMoviesRemoteSource
import io.filmtime.data.api.trakt.TraktSearchRemoteSource
import io.filmtime.data.api.trakt.TraktSyncRemoteSource
import io.filmtime.data.database.dao.MovieDetailDao
import io.filmtime.data.model.GeneralError
import io.filmtime.data.model.Person
Expand All @@ -19,8 +17,6 @@ import javax.inject.Inject

internal class TmdbMovieRepositoryImpl @Inject constructor(
private val tmdbMoviesRemoteSource: TmdbMoviesRemoteSource,
private val traktMovieSearchRemoteSource: TraktSearchRemoteSource,
private val traktSyncRemoteSource: TraktSyncRemoteSource,
private val movieDao: MovieDetailDao,
) : TmdbMovieRepository {

Expand All @@ -38,40 +34,11 @@ internal class TmdbMovieRepositoryImpl @Inject constructor(

private suspend fun fetchMovieDetailsFromNetwork(movieId: Int): Result<VideoDetail, GeneralError> {
return when (val result = tmdbMoviesRemoteSource.movieDetails(movieId)) {
is Result.Failure -> result
is Result.Success -> {
movieDao.storeMovie(result.data.toEntity())
when (val traktIdResult = traktMovieSearchRemoteSource.getByTmdbId(result.data.ids.tmdbId.toString())) {
is Result.Failure -> result
is Result.Success -> {
val traktId = traktIdResult.data.toInt()
when (val watched = traktSyncRemoteSource.getHistoryById(traktId.toString())) {
is Result.Failure -> result.run {
copy(
data = data.copy(
ids = data.ids.copy(
traktId = traktId,
),
),
)
}

is Result.Success -> {
result.run {
copy(
data = data.copy(
ids = data.ids.copy(
traktId = traktId,
),
isWatched = watched.data,
),
)
}
}
}
}
}
result
}
is Result.Failure -> result
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ package io.filmtime.data.trakt

import io.filmtime.data.model.GeneralError
import io.filmtime.data.model.Result
import io.filmtime.data.model.TraktHistory

interface TraktHistoryRepository {

suspend fun addToHistory(id: String): Result<Unit, GeneralError>
suspend fun isWatched(tmdbId: Int): Result<TraktHistory, GeneralError>

suspend fun addToHistory(traktId: Int): Result<Unit, GeneralError>

suspend fun removeFromHistory(traktId: Int): Result<Unit, GeneralError>
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
package io.filmtime.data.trakt

import io.filmtime.data.api.trakt.TraktSearchRemoteSource
import io.filmtime.data.api.trakt.TraktSyncRemoteSource
import io.filmtime.data.model.GeneralError
import io.filmtime.data.model.Result
import io.filmtime.data.model.TraktHistory
import javax.inject.Inject

class TraktHistoryRepositoryImpl @Inject constructor(
private val traktSyncRemoteSource: TraktSyncRemoteSource,
private val traktSearchRemoteSource: TraktSearchRemoteSource,
) : TraktHistoryRepository {
override suspend fun addToHistory(id: String): Result<Unit, GeneralError> {
return traktSyncRemoteSource.addToHistory(id)
}

override suspend fun isWatched(tmdbId: Int): Result<TraktHistory, GeneralError> =
when (val traktIdResult = traktSearchRemoteSource.getByTmdbId(tmdbId)) {
is Result.Failure -> traktIdResult
is Result.Success -> {
val traktId = traktIdResult.data
traktSyncRemoteSource.getHistoryById(traktId)
.mapSuccess { isWatched ->
TraktHistory(traktId, isWatched)
}
}
}

override suspend fun addToHistory(traktId: Int): Result<Unit, GeneralError> =
traktSyncRemoteSource.addToHistory(traktId)

override suspend fun removeFromHistory(traktId: Int): Result<Unit, GeneralError> =
traktSyncRemoteSource.removeFromHistory(traktId)
}
1 change: 1 addition & 0 deletions domain/trakt/history/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ android {
}

dependencies {
implementation(project(":data:trakt"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.filmtime.domain.trakt.history

import io.filmtime.data.model.GeneralError
import io.filmtime.data.model.Result

interface AddToHistoryUseCase {

suspend operator fun invoke(traktId: Int): Result<Unit, GeneralError>
}
Loading

0 comments on commit 0c3e08f

Please sign in to comment.